|
19 | 19 |
|
20 | 20 | #include "iceberg/expression/decimal.h" |
21 | 21 |
|
| 22 | +#include <algorithm> |
22 | 23 | #include <array> |
23 | 24 | #include <bit> |
24 | 25 | #include <cassert> |
25 | 26 | #include <charconv> |
26 | 27 | #include <cstddef> |
27 | 28 | #include <cstdint> |
28 | 29 | #include <format> |
| 30 | +#include <iomanip> |
29 | 31 | #include <iostream> |
30 | 32 | #include <limits> |
| 33 | +#include <sstream> |
31 | 34 | #include <string> |
32 | 35 | #include <string_view> |
33 | 36 |
|
@@ -622,9 +625,69 @@ static inline void ShiftAndAdd(std::string_view input, std::array<uint64_t, 2>& |
622 | 625 | } |
623 | 626 | } |
624 | 627 |
|
| 628 | +// Returns a mask for the bit_index lower order bits. |
| 629 | +// Only valid for bit_index in the range [0, 64). |
| 630 | +constexpr uint64_t LeastSignificantBitMask(int64_t bit_index) { |
| 631 | + return (static_cast<uint64_t>(1) << bit_index) - 1; |
| 632 | +} |
| 633 | + |
625 | 634 | static void AppendLittleEndianArrayToString(const std::array<uint64_t, 2>& array, |
626 | 635 | std::string* out) { |
627 | | - // TODO(zhjwpku): Implementation this. |
| 636 | + const auto most_significant_non_zero = std::ranges::find_if( |
| 637 | + array.rbegin(), array.rend(), [](uint64_t v) { return v != 0; }); |
| 638 | + if (most_significant_non_zero == array.rend()) { |
| 639 | + out->push_back('0'); |
| 640 | + return; |
| 641 | + } |
| 642 | + |
| 643 | + size_t most_significant_elem_idx = &*most_significant_non_zero - array.data(); |
| 644 | + std::array<uint64_t, 2> copy = array; |
| 645 | + constexpr uint32_t k1e9 = 1000000000U; |
| 646 | + constexpr size_t kNumBits = 128; |
| 647 | + // Segments will contain the array split into groups that map to decimal digits, in |
| 648 | + // little endian order. Each segment will hold at most 9 decimal digits. For example, if |
| 649 | + // the input represents 9876543210123456789, then segments will be [123456789, |
| 650 | + // 876543210, 9]. |
| 651 | + // The max number of segments needed = ceil(kNumBits * log(2) / log(1e9)) |
| 652 | + // = ceil(kNumBits / 29.897352854) <= ceil(kNumBits / 29). |
| 653 | + std::array<uint32_t, (kNumBits + 28) / 29> segments; |
| 654 | + size_t num_segments = 0; |
| 655 | + uint64_t* most_significant_elem = ©[most_significant_elem_idx]; |
| 656 | + |
| 657 | + std::cout << copy[1] << " " << copy[0] << std::endl; |
| 658 | + do { |
| 659 | + // Compute remainder = copy % 1e9 and copy = copy / 1e9. |
| 660 | + uint32_t remainder = 0; |
| 661 | + uint64_t* elem = most_significant_elem; |
| 662 | + do { |
| 663 | + // Compute dividend = (remainder << 32) | *elem (a virtual 96-bit integer); |
| 664 | + // *elem = dividend / 1e9; |
| 665 | + // remainder = dividend % 1e9. |
| 666 | + auto hi = static_cast<uint32_t>(*elem >> 32); |
| 667 | + auto lo = static_cast<uint32_t>(*elem & LeastSignificantBitMask(32)); |
| 668 | + uint64_t dividend_hi = (static_cast<uint64_t>(remainder) << 32) | hi; |
| 669 | + uint64_t quotient_hi = dividend_hi / k1e9; |
| 670 | + remainder = static_cast<uint32_t>(dividend_hi % k1e9); |
| 671 | + uint64_t dividend_lo = (static_cast<uint64_t>(remainder) << 32) | lo; |
| 672 | + uint64_t quotient_lo = dividend_lo / k1e9; |
| 673 | + remainder = static_cast<uint32_t>(dividend_lo % k1e9); |
| 674 | + *elem = (quotient_hi << 32) | quotient_lo; |
| 675 | + } while (elem-- != copy.data()); |
| 676 | + |
| 677 | + segments[num_segments++] = remainder; |
| 678 | + } while (*most_significant_elem != 0 || most_significant_elem-- != copy.data()); |
| 679 | + |
| 680 | + const uint32_t* segment = &segments[num_segments - 1]; |
| 681 | + std::stringstream oss; |
| 682 | + // First segment is formatted as-is. |
| 683 | + oss << *segment; |
| 684 | + // Remaining segments are formatted with leading zeros to fill 9 digits. e.g. 123 is |
| 685 | + // formatted as "000000123" |
| 686 | + while (segment != segments.data()) { |
| 687 | + --segment; |
| 688 | + oss << std::setfill('0') << std::setw(9) << *segment; |
| 689 | + } |
| 690 | + out->append(oss.str()); |
628 | 691 | } |
629 | 692 |
|
630 | 693 | } // namespace |
@@ -689,8 +752,6 @@ Result<Decimal> Decimal::FromString(std::string_view str, int32_t* precision, |
689 | 752 | result.Negate(); |
690 | 753 | } |
691 | 754 |
|
692 | | - std::cout << result_array[1] << " " << result_array[0] << std::endl; |
693 | | - |
694 | 755 | if (parsed_scale < 0) { |
695 | 756 | // For the scale to 0, to avoid negative scales (due to compatibility issues with |
696 | 757 | // external systems such as databases) |
@@ -798,13 +859,11 @@ ICEBERG_EXPORT Decimal operator*(const Decimal& lhs, const Decimal& rhs) { |
798 | 859 | } |
799 | 860 |
|
800 | 861 | ICEBERG_EXPORT Decimal operator/(const Decimal& lhs, const Decimal& rhs) { |
801 | | - auto [result, _] = lhs.Divide(rhs).value(); |
802 | | - return result; |
| 862 | + return lhs.Divide(rhs).value().first; |
803 | 863 | } |
804 | 864 |
|
805 | 865 | ICEBERG_EXPORT Decimal operator%(const Decimal& lhs, const Decimal& rhs) { |
806 | | - auto [_, remainder] = lhs.Divide(rhs).value(); |
807 | | - return remainder; |
| 866 | + return lhs.Divide(rhs).value().second; |
808 | 867 | } |
809 | 868 |
|
810 | 869 | ICEBERG_EXPORT bool operator<(const Decimal& lhs, const Decimal& rhs) { |
|
0 commit comments