In Java - what is the faster way to find if the given integer number is starting with the digit 2 without having to convert the number into a string?
String.valueOf(number).charAt(0) == '2'
If you wanted to avoid converting it to a string, you could just keep dividing by 10 to find the most significant digit:
int getMostSignificantDigit(int x)
{
// Need to handle Integer.MIN_VALUE "specially" as the absolute value can't
// represented. We can hard-code the fact that it starts with 2 :)
x = x == Integer.MIN_VALUE ? 2 : Math.abs(x);
while (x >= 10)
{
x = x / 10;
}
return x;
}
I don't know whether this would be faster than Husman's log/pow approach.
I've pitted the various solutions against each other:
public class FirstDigit
{
static int digit;
@GenerateMicroBenchmark public void string() {
for (int i = 200_000_000; i < 400_000_000; i += 999961)
digit = Integer.toString(i).charAt(0);
}
@GenerateMicroBenchmark public void math() {
for (int i = 200_000_000; i < 400_000_000; i += 999961) {
digit = (int) floor(i / pow(10, floor(log10(i))));
}
}
@GenerateMicroBenchmark public void divide() {
for (int i = 200_000_000; i < 400_000_000; i += 999961) {
int x = i;
while (x > 10) x /= 10;
digit = x;
}
}
@GenerateMicroBenchmark public void brokenDivide() {
for (int i = 200_000_000; i < 400_000_000; i += 999961) {
int x = i;
while (x > 10) x >>= 3;
digit = x;
}
}
@GenerateMicroBenchmark public void bitTwiddling() {
for (int i = 200_000_000; i < 400_000_000; i += 999961) {
digit = i/powersOf10[log10(i)];
}
}
@GenerateMicroBenchmark public boolean avoidDivide() {
boolean b = true;
for (int i = 200_000_000; i < 400_000_000; i += 999961) {
b ^= firstDigitIsTwo(i);
}
return b;
}
private static final int[] log256 = new int[256];
static {
for (int i = 0; i < 256; i++) log256[i] = 1 + log256[i / 2];
log256[0] = -1;
}
private static int powersOf10[] = {1, 10, 100, 1000, 10_000, 100_000,
1_000_000, 10_000_000, 100_000_000, 1_000_000_000};
public static int log2(int v) {
int t, tt;
return ((tt = v >> 16) != 0)?
(t = tt >> 8) != 0 ? 24 + log256[t] : 16 + log256[tt]
: (t = v >> 8) != 0 ? 8 + log256[t] : log256[v];
}
public static int log10(int v) {
int t = (log2(v) + 1) * 1233 >> 12;
return t - (v < powersOf10[t] ? 1 : 0);
}
static final int [] limits = new int[] {
2_000_000_000, Integer.MAX_VALUE,
200_000_000, 300_000_000-1,
20_000_000, 30_000_000-1,
2_000_000, 3_000_000-1,
200_000, 300_000-1,
20_000, 30_000-1,
2000, 3000-1,
200, 300-1,
20, 30-1,
2, 3-1,
};
public static boolean firstDigitIsTwo(int v) {
for ( int i = 0; i < limits.length; i+= 2) {
if ( v > limits[i+1] ) return false;
if ( v >= limits[i] ) return true;
}
return false;
}
}
And the results:
Benchmark Mode Thr Cnt Sec Mean Mean error Units
FirstDigit.avoidDivide thrpt 1 3 5 2324.271 58.145 ops/msec
FirstDigit.bitTwiddling thrpt 1 3 5 716.453 6.407 ops/msec
FirstDigit.brokenDivide thrpt 1 3 5 578.259 7.534 ops/msec
o.s.FirstDigit.divide thrpt 1 3 5 125.509 2.323 ops/msec
o.s.FirstDigit.string thrpt 1 3 5 78.233 2.030 ops/msec
o.s.FirstDigit.math thrpt 1 3 5 14.226 0.034 ops/msec
math
approach is a clear loser;string
approach beats math
by a factor of six;divide
algorithm is 60% faster than that;bitTwiddling
algorithm is yet another six times faster than divide
;avoidDivide
approach (which gives a yes/no answer directly, unlike all others, which actually determine the first digit) is another three times faster than the bitTwiddiling
algo and is the undisputed winner;brokenDivide
algo. Instead of dividing by ten, it just shifts by three, giving the wrong answer. The point is to underline where the bottleneck with the divide
algorithm is: brokenDivide
is 4.6 times faster than divide
, and just 0.2 times slower than bitTwiddling
.Note that I have used quite large numbers; the relative speeds vary with the magnitude.
I would be tempted to do something like this:
x = Math.abs(x);
if ( ((int) Math.floor(x / Math.pow(10, Math.floor(Math.log10(x))))) == 2 )
{
... // x equals 2
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With