10 changes: 10 additions & 0 deletions libc/test/src/__support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,13 @@ add_libc_unittest(
DEPENDS
libc.src.__support.common
)

add_libc_unittest(
high_precision_decimal_test
SUITE
libc_support_unittests
SRCS
high_precision_decimal_test.cpp
DEPENDS
libc.src.__support.high_precision_decimal
)
381 changes: 381 additions & 0 deletions libc/test/src/__support/high_precision_decimal_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,381 @@
//===-- Unittests for high_precision_decimal ------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/__support/high_precision_decimal.h"

#include "utils/UnitTest/Test.h"

TEST(LlvmLibcHighPrecisionDecimalTest, BasicInit) {
__llvm_libc::internal::HighPrecsisionDecimal hpd =
__llvm_libc::internal::HighPrecsisionDecimal("1.2345");
uint8_t *digits = hpd.getDigits();

EXPECT_EQ(digits[0], uint8_t(1));
EXPECT_EQ(digits[1], uint8_t(2));
EXPECT_EQ(digits[2], uint8_t(3));
EXPECT_EQ(digits[3], uint8_t(4));
EXPECT_EQ(digits[4], uint8_t(5));
EXPECT_EQ(hpd.getNumDigits(), 5u);
EXPECT_EQ(hpd.getDecimalPoint(), 1);
}

TEST(LlvmLibcHighPrecisionDecimalTest, BasicShift) {
__llvm_libc::internal::HighPrecsisionDecimal hpd =
__llvm_libc::internal::HighPrecsisionDecimal("1");
uint8_t *digits = hpd.getDigits();

hpd.shift(1); // shift left 1, equal to multiplying by 2.

EXPECT_EQ(digits[0], uint8_t(2));
EXPECT_EQ(hpd.getNumDigits(), 1u);
EXPECT_EQ(hpd.getDecimalPoint(), 1);
}

TEST(LlvmLibcHighPrecisionDecimalTest, SmallShift) {
__llvm_libc::internal::HighPrecsisionDecimal hpd =
__llvm_libc::internal::HighPrecsisionDecimal("1.2345");
uint8_t *digits = hpd.getDigits();

hpd.shift(-1); // shift right one, equal to dividing by 2
// result should be 0.61725

EXPECT_EQ(digits[0], uint8_t(6));
EXPECT_EQ(digits[1], uint8_t(1));
EXPECT_EQ(digits[2], uint8_t(7));
EXPECT_EQ(digits[3], uint8_t(2));
EXPECT_EQ(digits[4], uint8_t(5));
EXPECT_EQ(hpd.getNumDigits(), 5u);
EXPECT_EQ(hpd.getDecimalPoint(), 0);

hpd.shift(1); // shift left one, equal to multiplying by 2
// result should be 1.2345 again

EXPECT_EQ(digits[0], uint8_t(1));
EXPECT_EQ(digits[1], uint8_t(2));
EXPECT_EQ(digits[2], uint8_t(3));
EXPECT_EQ(digits[3], uint8_t(4));
EXPECT_EQ(digits[4], uint8_t(5));
EXPECT_EQ(hpd.getNumDigits(), 5u);
EXPECT_EQ(hpd.getDecimalPoint(), 1);

hpd.shift(1); // shift left one again
// result should be 2.469

EXPECT_EQ(digits[0], uint8_t(2));
EXPECT_EQ(digits[1], uint8_t(4));
EXPECT_EQ(digits[2], uint8_t(6));
EXPECT_EQ(digits[3], uint8_t(9));
EXPECT_EQ(hpd.getNumDigits(), 4u);
EXPECT_EQ(hpd.getDecimalPoint(), 1);

hpd.shift(-1); // shift right one again
// result should be 1.2345 again

EXPECT_EQ(digits[0], uint8_t(1));
EXPECT_EQ(digits[1], uint8_t(2));
EXPECT_EQ(digits[2], uint8_t(3));
EXPECT_EQ(digits[3], uint8_t(4));
EXPECT_EQ(digits[4], uint8_t(5));
EXPECT_EQ(hpd.getNumDigits(), 5u);
EXPECT_EQ(hpd.getDecimalPoint(), 1);
}

TEST(LlvmLibcHighPrecisionDecimalTest, MediumShift) {
__llvm_libc::internal::HighPrecsisionDecimal hpd =
__llvm_libc::internal::HighPrecsisionDecimal(".299792458");
uint8_t *digits = hpd.getDigits();

hpd.shift(-3); // shift right three, equal to dividing by 8
// result should be 0.03747405725

EXPECT_EQ(digits[0], uint8_t(3));
EXPECT_EQ(digits[1], uint8_t(7));
EXPECT_EQ(digits[2], uint8_t(4));
EXPECT_EQ(digits[3], uint8_t(7));
EXPECT_EQ(digits[4], uint8_t(4));
EXPECT_EQ(digits[5], uint8_t(0));
EXPECT_EQ(digits[6], uint8_t(5));
EXPECT_EQ(digits[7], uint8_t(7));
EXPECT_EQ(digits[8], uint8_t(2));
EXPECT_EQ(digits[9], uint8_t(5));
EXPECT_EQ(hpd.getNumDigits(), 10u);
EXPECT_EQ(hpd.getDecimalPoint(), -1);

hpd.shift(3); // shift left three, equal to multiplying by 8
// result should be 0.299792458 again

EXPECT_EQ(digits[0], uint8_t(2));
EXPECT_EQ(digits[1], uint8_t(9));
EXPECT_EQ(digits[2], uint8_t(9));
EXPECT_EQ(digits[3], uint8_t(7));
EXPECT_EQ(digits[4], uint8_t(9));
EXPECT_EQ(digits[5], uint8_t(2));
EXPECT_EQ(digits[6], uint8_t(4));
EXPECT_EQ(digits[7], uint8_t(5));
EXPECT_EQ(digits[8], uint8_t(8));
EXPECT_EQ(hpd.getNumDigits(), 9u);
EXPECT_EQ(hpd.getDecimalPoint(), 0);
}

TEST(LlvmLibcHighPrecisionDecimalTest, BigShift) {
__llvm_libc::internal::HighPrecsisionDecimal hpd =
__llvm_libc::internal::HighPrecsisionDecimal(".299792458");
uint8_t *digits = hpd.getDigits();

hpd.shift(-29); // shift right 29, equal to dividing by 536,870,912
// result should be 0.0000000005584069676697254180908203125

EXPECT_EQ(digits[0], uint8_t(5));
EXPECT_EQ(digits[1], uint8_t(5));
EXPECT_EQ(digits[2], uint8_t(8));
EXPECT_EQ(digits[3], uint8_t(4));
EXPECT_EQ(digits[4], uint8_t(0));
EXPECT_EQ(digits[5], uint8_t(6));
EXPECT_EQ(digits[6], uint8_t(9));
EXPECT_EQ(digits[7], uint8_t(6));
EXPECT_EQ(digits[8], uint8_t(7));
EXPECT_EQ(digits[9], uint8_t(6));
EXPECT_EQ(digits[10], uint8_t(6));
EXPECT_EQ(digits[11], uint8_t(9));
EXPECT_EQ(digits[12], uint8_t(7));
EXPECT_EQ(digits[13], uint8_t(2));
EXPECT_EQ(digits[14], uint8_t(5));
EXPECT_EQ(digits[15], uint8_t(4));
EXPECT_EQ(digits[16], uint8_t(1));
EXPECT_EQ(digits[17], uint8_t(8));
EXPECT_EQ(digits[18], uint8_t(0));
EXPECT_EQ(digits[19], uint8_t(9));
EXPECT_EQ(digits[20], uint8_t(0));
EXPECT_EQ(digits[21], uint8_t(8));
EXPECT_EQ(digits[22], uint8_t(2));
EXPECT_EQ(digits[23], uint8_t(0));
EXPECT_EQ(digits[24], uint8_t(3));
EXPECT_EQ(digits[25], uint8_t(1));
EXPECT_EQ(digits[26], uint8_t(2));
EXPECT_EQ(digits[27], uint8_t(5));
EXPECT_EQ(hpd.getNumDigits(), 28u);
EXPECT_EQ(hpd.getDecimalPoint(), -9);

hpd.shift(29); // shift left 29, equal to multiplying by 536,870,912
// result should be 0.299792458 again

EXPECT_EQ(digits[0], uint8_t(2));
EXPECT_EQ(digits[1], uint8_t(9));
EXPECT_EQ(digits[2], uint8_t(9));
EXPECT_EQ(digits[3], uint8_t(7));
EXPECT_EQ(digits[4], uint8_t(9));
EXPECT_EQ(digits[5], uint8_t(2));
EXPECT_EQ(digits[6], uint8_t(4));
EXPECT_EQ(digits[7], uint8_t(5));
EXPECT_EQ(digits[8], uint8_t(8));
EXPECT_EQ(hpd.getNumDigits(), 9u);
EXPECT_EQ(hpd.getDecimalPoint(), 0);
}

TEST(LlvmLibcHighPrecisionDecimalTest, BigShiftInSteps) {
__llvm_libc::internal::HighPrecsisionDecimal hpd =
__llvm_libc::internal::HighPrecsisionDecimal("1");
uint8_t *digits = hpd.getDigits();

hpd.shift(60); // shift left 60, equal to multiplying by
// 1152921504606846976.

EXPECT_EQ(digits[0], uint8_t(1));
EXPECT_EQ(digits[1], uint8_t(1));
EXPECT_EQ(digits[2], uint8_t(5));
EXPECT_EQ(digits[3], uint8_t(2));
EXPECT_EQ(digits[4], uint8_t(9));
EXPECT_EQ(digits[5], uint8_t(2));
EXPECT_EQ(digits[6], uint8_t(1));
EXPECT_EQ(digits[7], uint8_t(5));
EXPECT_EQ(digits[8], uint8_t(0));
EXPECT_EQ(digits[9], uint8_t(4));
EXPECT_EQ(digits[10], uint8_t(6));
EXPECT_EQ(digits[11], uint8_t(0));
EXPECT_EQ(digits[12], uint8_t(6));
EXPECT_EQ(digits[13], uint8_t(8));
EXPECT_EQ(digits[14], uint8_t(4));
EXPECT_EQ(digits[15], uint8_t(6));
EXPECT_EQ(digits[16], uint8_t(9));
EXPECT_EQ(digits[17], uint8_t(7));
EXPECT_EQ(digits[18], uint8_t(6));
EXPECT_EQ(hpd.getNumDigits(), 19u);
EXPECT_EQ(hpd.getDecimalPoint(), 19);

hpd.shift(40); // shift left 40, equal to multiplying by
// 1099511627776. Result should be 2^100

EXPECT_EQ(digits[0], uint8_t(1));
EXPECT_EQ(digits[1], uint8_t(2));
EXPECT_EQ(digits[2], uint8_t(6));
EXPECT_EQ(digits[3], uint8_t(7));
EXPECT_EQ(digits[4], uint8_t(6));
EXPECT_EQ(digits[5], uint8_t(5));
EXPECT_EQ(digits[6], uint8_t(0));
EXPECT_EQ(digits[7], uint8_t(6));
EXPECT_EQ(digits[8], uint8_t(0));
EXPECT_EQ(digits[9], uint8_t(0));
EXPECT_EQ(digits[10], uint8_t(2));
EXPECT_EQ(digits[11], uint8_t(2));
EXPECT_EQ(digits[12], uint8_t(8));
EXPECT_EQ(digits[13], uint8_t(2));
EXPECT_EQ(digits[14], uint8_t(2));
EXPECT_EQ(digits[15], uint8_t(9));
EXPECT_EQ(digits[16], uint8_t(4));
EXPECT_EQ(digits[17], uint8_t(0));
EXPECT_EQ(digits[18], uint8_t(1));
EXPECT_EQ(digits[19], uint8_t(4));
EXPECT_EQ(digits[20], uint8_t(9));
EXPECT_EQ(digits[21], uint8_t(6));
EXPECT_EQ(digits[22], uint8_t(7));
EXPECT_EQ(digits[23], uint8_t(0));
EXPECT_EQ(digits[24], uint8_t(3));
EXPECT_EQ(digits[25], uint8_t(2));
EXPECT_EQ(digits[26], uint8_t(0));
EXPECT_EQ(digits[27], uint8_t(5));
EXPECT_EQ(digits[28], uint8_t(3));
EXPECT_EQ(digits[29], uint8_t(7));
EXPECT_EQ(digits[30], uint8_t(6));

EXPECT_EQ(hpd.getNumDigits(), 31u);
EXPECT_EQ(hpd.getDecimalPoint(), 31);

hpd.shift(-60); // shift right 60, equal to dividing by
// 1152921504606846976. Result should be 2^40

EXPECT_EQ(digits[0], uint8_t(1));
EXPECT_EQ(digits[1], uint8_t(0));
EXPECT_EQ(digits[2], uint8_t(9));
EXPECT_EQ(digits[3], uint8_t(9));
EXPECT_EQ(digits[4], uint8_t(5));
EXPECT_EQ(digits[5], uint8_t(1));
EXPECT_EQ(digits[6], uint8_t(1));
EXPECT_EQ(digits[7], uint8_t(6));
EXPECT_EQ(digits[8], uint8_t(2));
EXPECT_EQ(digits[9], uint8_t(7));
EXPECT_EQ(digits[10], uint8_t(7));
EXPECT_EQ(digits[11], uint8_t(7));
EXPECT_EQ(digits[12], uint8_t(6));

EXPECT_EQ(hpd.getNumDigits(), 13u);
EXPECT_EQ(hpd.getDecimalPoint(), 13);

hpd.shift(-40); // shift right 40, equal to dividing by
// 1099511627776. Result should be 1

EXPECT_EQ(digits[0], uint8_t(1));

EXPECT_EQ(hpd.getNumDigits(), 1u);
EXPECT_EQ(hpd.getDecimalPoint(), 1);
}

TEST(LlvmLibcHighPrecisionDecimalTest, VeryBigShift) {
__llvm_libc::internal::HighPrecsisionDecimal hpd =
__llvm_libc::internal::HighPrecsisionDecimal("1");
uint8_t *digits = hpd.getDigits();

hpd.shift(100); // shift left 100, equal to multiplying by
// 1267650600228229401496703205376.
// result should be 2^100

EXPECT_EQ(digits[0], uint8_t(1));
EXPECT_EQ(digits[1], uint8_t(2));
EXPECT_EQ(digits[2], uint8_t(6));
EXPECT_EQ(digits[3], uint8_t(7));
EXPECT_EQ(digits[4], uint8_t(6));
EXPECT_EQ(digits[5], uint8_t(5));
EXPECT_EQ(digits[6], uint8_t(0));
EXPECT_EQ(digits[7], uint8_t(6));
EXPECT_EQ(digits[8], uint8_t(0));
EXPECT_EQ(digits[9], uint8_t(0));
EXPECT_EQ(digits[10], uint8_t(2));
EXPECT_EQ(digits[11], uint8_t(2));
EXPECT_EQ(digits[12], uint8_t(8));
EXPECT_EQ(digits[13], uint8_t(2));
EXPECT_EQ(digits[14], uint8_t(2));
EXPECT_EQ(digits[15], uint8_t(9));
EXPECT_EQ(digits[16], uint8_t(4));
EXPECT_EQ(digits[17], uint8_t(0));
EXPECT_EQ(digits[18], uint8_t(1));
EXPECT_EQ(digits[19], uint8_t(4));
EXPECT_EQ(digits[20], uint8_t(9));
EXPECT_EQ(digits[21], uint8_t(6));
EXPECT_EQ(digits[22], uint8_t(7));
EXPECT_EQ(digits[23], uint8_t(0));
EXPECT_EQ(digits[24], uint8_t(3));
EXPECT_EQ(digits[25], uint8_t(2));
EXPECT_EQ(digits[26], uint8_t(0));
EXPECT_EQ(digits[27], uint8_t(5));
EXPECT_EQ(digits[28], uint8_t(3));
EXPECT_EQ(digits[29], uint8_t(7));
EXPECT_EQ(digits[30], uint8_t(6));

EXPECT_EQ(hpd.getNumDigits(), 31u);
EXPECT_EQ(hpd.getDecimalPoint(), 31);

hpd.shift(-100); // shift right 100, equal to dividing by
// 1267650600228229401496703205376.
// result should be 1

EXPECT_EQ(digits[0], uint8_t(1));
EXPECT_EQ(hpd.getNumDigits(), 1u);
EXPECT_EQ(hpd.getDecimalPoint(), 1);
}

TEST(LlvmLibcHighPrecisionDecimalTest, RoundingTest) {
__llvm_libc::internal::HighPrecsisionDecimal hpd =
__llvm_libc::internal::HighPrecsisionDecimal("1.2345");

EXPECT_EQ(hpd.roundToIntegerType<uint32_t>(), uint32_t(1));
EXPECT_EQ(hpd.roundToIntegerType<uint64_t>(), uint64_t(1));
EXPECT_EQ(hpd.roundToIntegerType<__uint128_t>(), __uint128_t(1));

hpd.shift(1); // shift left 1 to get 2.469 (rounds to 2)

EXPECT_EQ(hpd.roundToIntegerType<uint32_t>(), uint32_t(2));
EXPECT_EQ(hpd.roundToIntegerType<uint64_t>(), uint64_t(2));
EXPECT_EQ(hpd.roundToIntegerType<__uint128_t>(), __uint128_t(2));

hpd.shift(1); // shift left 1 to get 4.938 (rounds to 5)

EXPECT_EQ(hpd.roundToIntegerType<uint32_t>(), uint32_t(5));
EXPECT_EQ(hpd.roundToIntegerType<uint64_t>(), uint64_t(5));
EXPECT_EQ(hpd.roundToIntegerType<__uint128_t>(), __uint128_t(5));

// 2.5 is right between two integers, so we round to even (2)
hpd = __llvm_libc::internal::HighPrecsisionDecimal("2.5");

EXPECT_EQ(hpd.roundToIntegerType<uint32_t>(), uint32_t(2));
EXPECT_EQ(hpd.roundToIntegerType<uint64_t>(), uint64_t(2));
EXPECT_EQ(hpd.roundToIntegerType<__uint128_t>(), __uint128_t(2));

// unless it's marked as having truncated, which means it's actually slightly
// higher, forcing a round up (3)
hpd.setTruncated(true);

EXPECT_EQ(hpd.roundToIntegerType<uint32_t>(), uint32_t(3));
EXPECT_EQ(hpd.roundToIntegerType<uint64_t>(), uint64_t(3));
EXPECT_EQ(hpd.roundToIntegerType<__uint128_t>(), __uint128_t(3));

// Check that the larger int types are being handled properly (overflow is not
// handled, so int types that are too small are ignored for this test.)

// 1099511627776 = 2^40
hpd = __llvm_libc::internal::HighPrecsisionDecimal("1099511627776");

EXPECT_EQ(hpd.roundToIntegerType<uint64_t>(), uint64_t(1099511627776));
EXPECT_EQ(hpd.roundToIntegerType<__uint128_t>(), __uint128_t(1099511627776));

// 1267650600228229401496703205376 = 2^100
hpd = __llvm_libc::internal::HighPrecsisionDecimal(
"1267650600228229401496703205376");

__uint128_t result = __uint128_t(1) << 100;

EXPECT_EQ(hpd.roundToIntegerType<__uint128_t>(), result);
}
65 changes: 65 additions & 0 deletions libc/utils/mathtools/GenerateHPDConstants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from math import *

'''
This script is used to generate a table used by
libc/src/__support/high_precision_decimal.h.
For the ith entry in the table there are two values (indexed starting at 0).
The first value is the number of digits longer the second value would be if
multiplied by 2^i.
The second value is the smallest number that would create that number of
additional digits (which in base ten is always 5^i). Anything less creates one
fewer digit.
As an example, the 3rd entry in the table is {1, "125"}. This means that if
125 is multiplied by 2^3 = 8, it will have exactly one more digit.
Multiplying it out we get 125 * 8 = 1000. 125 is the smallest number that gives
that extra digit, for example 124 * 8 = 992, and all larger 3 digit numbers
also give only one extra digit when multiplied by 8, for example 8 * 999 = 7992.
This makes sense because 5^3 * 2^3 = 10^3, the smallest 4 digit number.
For numbers with more digits we can ignore the digits past what's in the second
value, since the most significant digits determine how many extra digits there
will be. Looking at the previous example, if we have 1000, and we look at just
the first 3 digits (since 125 has 3 digits), we see that 100 < 125, so we get
one fewer than 1 extra digits, which is 0.
Multiplying it out we get 1000 * 8 = 8000, which fits the expectation.
Another few quick examples:
For 1255, 125 !< 125, so 1 digit more: 1255 * 8 = 10040
For 9999, 999 !< 125, so 1 digit more: 9999 * 8 = 79992
Now let's try an example with the 10th entry: {4, "9765625"}. This one means
that 9765625 * 2^10 will have 4 extra digits.
Let's skip straight to the examples:
For 1, 1 < 9765625, so 4-1=3 extra digits: 1 * 2^10 = 1024, 1 digit to 4 digits is a difference of 3.
For 9765624, 9765624 < 9765625 so 3 extra digits: 9765624 * 1024 = 9999998976, 7 digits to 10 digits is a difference of 3.
For 9765625, 9765625 !< 9765625 so 4 extra digits: 9765625 * 1024 = 10000000000, 7 digits to 11 digits is a difference of 4.
For 9999999, 9999999 !< 9765625 so 4 extra digits: 9999999 * 1024 = 10239998976, 7 digits to 11 digits is a difference of 4.
For 12345678, 1234567 < 9765625 so 3 extra digits: 12345678 * 1024 = 12641974272, 8 digits to 11 digits is a difference of 3.
This is important when left shifting in the HPD class because it reads and
writes the number backwards, and to shift in place it needs to know where the
last digit should go. Since a binary left shift by i bits is the same as
multiplying by 2^i we know that looking up the ith element in the table will
tell us the number of additional digits. If the first digits of the number
being shifted are greater than or equal to the digits of 5^i (the second value
of each entry) then it is just the first value in the entry, else it is one
fewer.
'''


# Generate Left Shift Table
outStr = ""
for i in range(61):
tenToTheI = 10**i
fiveToTheI = 5**i
outStr += "{"
# The number of new digits that would be created by multiplying 5**i by 2**i
outStr += str(ceil(log10(tenToTheI) - log10(fiveToTheI)))
outStr += ', "'
if not i == 0:
outStr += str(fiveToTheI)
outStr += '"},\n'

print(outStr)