Add general math function to get number of base 10 digits required

This commit is contained in:
Jonathan G Rennison 2024-08-31 11:02:33 +01:00
parent 282f13cab0
commit 4ce8a36e4d
4 changed files with 70 additions and 5 deletions

View File

@ -147,3 +147,23 @@ uint32_t RXDecompressUint(uint16_t num)
if (num > 0x100) return ((num - 0x100) << 3) + 0x100;
return num;
}
/* Algorithm from https://lemire.me/blog/2021/05/28/computing-the-number-of-digits-of-an-integer-quickly/ */
uint GetBase10DigitsRequired32(uint32_t x)
{
static uint32_t table[] = {9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999};
int log2 = std::countl_zero<uint32_t>(1) - std::countl_zero<uint32_t>(x | 1);
int log10approx = (9 * log2) >> 5;
if (x > table[log10approx]) log10approx++;
return log10approx + 1;
}
uint GetBase10DigitsRequired64(uint64_t x)
{
/* Rather than using a huge lookup table for 64 bit values, use a loop */
uint64_t threshold = 10;
for (uint i = 1; i < 20; i++, threshold *= 10) {
if (x < threshold) return i;
}
return 20; // Largest number of digits required for uint64_t
}

View File

@ -433,6 +433,21 @@ constexpr inline T SaturatingAdd(T a, T b)
#endif
}
/**
* Return number of base 10 digits required for an unsigned value.
*/
template<typename T, std::enable_if_t<std::is_unsigned_v<T>, int> = 0>
constexpr inline uint GetBase10DigitsRequired(T x)
{
if (sizeof(T) <= sizeof(uint32_t) || x <= UINT32_MAX) {
extern uint GetBase10DigitsRequired32(uint32_t x);
return GetBase10DigitsRequired32(static_cast<uint32_t>(x));
} else {
extern uint GetBase10DigitsRequired64(uint64_t x);
return GetBase10DigitsRequired64(x);
}
}
uint32_t IntSqrt(uint32_t num);
uint32_t IntSqrt64(uint64_t num);

View File

@ -110,11 +110,7 @@ StringParameter *StringParameters::GetNextParameterPointer()
*/
void SetDParamMaxValue(size_t n, uint64_t max_value, uint min_count, FontSize size)
{
uint num_digits = 1;
while (max_value >= 10) {
num_digits++;
max_value /= 10;
}
uint num_digits = GetBase10DigitsRequired(max_value);
SetDParamMaxDigits(n, std::max(min_count, num_digits), size);
}

View File

@ -105,3 +105,37 @@ TEST_CASE("SaturatingAdd")
CHECK(SaturatingAdd<uint8_t>(255, 1) == 255);
CHECK(SaturatingAdd<uint8_t>(0, 254) == 254);
}
TEST_CASE("GetBase10DigitsRequired")
{
CHECK(GetBase10DigitsRequired<uint32_t>(0) == 1);
CHECK(GetBase10DigitsRequired<uint32_t>(1) == 1);
CHECK(GetBase10DigitsRequired<uint32_t>(9) == 1);
CHECK(GetBase10DigitsRequired<uint32_t>(10) == 2);
CHECK(GetBase10DigitsRequired<uint32_t>(99) == 2);
CHECK(GetBase10DigitsRequired<uint32_t>(100) == 3);
CHECK(GetBase10DigitsRequired<uint32_t>(999) == 3);
CHECK(GetBase10DigitsRequired<uint32_t>(1000) == 4);
CHECK(GetBase10DigitsRequired<uint32_t>(9999) == 4);
CHECK(GetBase10DigitsRequired<uint32_t>(10000) == 5);
CHECK(GetBase10DigitsRequired<uint32_t>(99999) == 5);
CHECK(GetBase10DigitsRequired<uint32_t>(100000) == 6);
CHECK(GetBase10DigitsRequired<uint32_t>(999999) == 6);
CHECK(GetBase10DigitsRequired<uint32_t>(1000000) == 7);
CHECK(GetBase10DigitsRequired<uint32_t>(9999999) == 7);
CHECK(GetBase10DigitsRequired<uint32_t>(10000000) == 8);
CHECK(GetBase10DigitsRequired<uint32_t>(99999999) == 8);
CHECK(GetBase10DigitsRequired<uint32_t>(100000000) == 9);
CHECK(GetBase10DigitsRequired<uint32_t>(999999999) == 9);
CHECK(GetBase10DigitsRequired<uint32_t>(1000000000) == 10);
CHECK(GetBase10DigitsRequired<uint32_t>(UINT32_MAX) == 10);
CHECK(GetBase10DigitsRequired<uint64_t>(9999999999ULL) == 10);
CHECK(GetBase10DigitsRequired<uint64_t>(10000000000ULL) == 11);
CHECK(GetBase10DigitsRequired<uint64_t>(99999999999ULL) == 11);
CHECK(GetBase10DigitsRequired<uint64_t>(100000000000ULL) == 12);
CHECK(GetBase10DigitsRequired<uint64_t>(999999999999ULL) == 12);
CHECK(GetBase10DigitsRequired<uint64_t>(1000000000000000000ULL) == 19);
CHECK(GetBase10DigitsRequired<uint64_t>(9999999999999999999ULL) == 19);
CHECK(GetBase10DigitsRequired<uint64_t>(10000000000000000000ULL) == 20);
CHECK(GetBase10DigitsRequired<uint64_t>(UINT64_MAX) == 20);
}