Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the fastest way to obtain the highest decimal digit of an integer? [duplicate]

What's the fastest way to implement

template <typename T>
unsigned highest_decimal_digit(T x);

(which returns e.g. 3 for 356431, 7 for 71 and 9 for 9)?

The best I can think of is:

  • constexpr-calculate the "middle-size" power of 10 which fits into T.
  • perform a binary search (over the powers of 10, possibly using a constexpr-constructed lookup table) to find p, the highest power-of-10 lower than x.
  • return x divided by p

... but maybe there's another approach.

Notes:

  • I expressed the question and my approach in C++14ish terms, and a solution in code would be nice, but an abstract solution (or even a solution in x86_64 assembly) would be fine. I do want something that will work for all (unsigned) integer types, though.
  • You may ignore signed integral types.
  • I didn't specify what "fast" is, but be hardware-conscious please.
like image 451
einpoklum Avatar asked Jul 28 '16 09:07

einpoklum


People also ask

What is the highest value of decimal?

There are a total of 10 digits in the decimal number system. Hence, the largest digit in the decimal number system is from one of the above. Clearly, out of the ones given here, 9 is the largest digit. Thus, 9 is the largest digit in the decimal number system.

What is the range of decimal?

The range of a decimal variable or the numbers in a decimal column is -n to +n , where n is the largest positive number that can be represented with the applicable precision and scale. The maximum range is 1 - 10³¹ to 10³¹ - 1.

How many bytes are used for decimal data type?

The maximum number of bytes the database server uses to store a decimal value is 17. One byte is used to store the exponent and sign, leaving 16 bytes to store up to 32 digits of precision.


2 Answers

The best option seems to be to combine CLZ approach and divide by precalculated power of 10. So, in pseudocode:

powers10=[1,1,1,1,10,10,10,10,100,100...]; // contains powers of 10 map to CLZ values
int firstDigit(unsigned INT_TYPE a) {
    if (a<10) return a; // one digit, screw it
    int j=typesize(a)-clz(a);
    if (j==3) return 1; // 10-16 hit this, return 1
    int k=a/powers10[j];
    if (k>9) return 1; else return k;
}

typesize() returns 64 for long long, 32 for int and 16 for short int.

like image 58
Vesper Avatar answered Nov 15 '22 08:11

Vesper


Newer x86 chips support an lzcnt instruction that tells you the number of clear bits at the start of an integer. You can access it using built-in compiler functions such as the following (from GCC):

 unsigned short __builtin_ia32_lzcnt_16(unsigned short);
 unsigned int __builtin_ia32_lzcnt_u32(unsigned int);
 unsigned long long __builtin_ia32_lzcnt_u64 (unsigned long long);

You could combine this with a lookup table of 640 values indicating the lower and upper bounds of integers starting with each digit from 0-9 that start with the corresponding number of clear bits. In fact, you could save space by right-shifting the lzcnt value 3 places; the correspondence with first decimal digits will still be unique.

like image 45
r3mainer Avatar answered Nov 15 '22 08:11

r3mainer