blob: 09fee1db4b64e1c4ded586158374949aa93d72f0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "iceberg/util/decimal.h"
#include <algorithm>
#include <array>
#include <bit>
#include <cstdint>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "iceberg/test/matchers.h"
#include "iceberg/util/int128.h"
namespace iceberg {
namespace {
void AssertDecimalFromString(const std::string& s, const Decimal& expected,
int32_t expected_precision, int32_t expected_scale) {
int32_t precision = 0;
int32_t scale = 0;
auto result = Decimal::FromString(s, &precision, &scale);
EXPECT_THAT(result, IsOk());
const Decimal& actual = result.value();
EXPECT_EQ(expected, actual);
EXPECT_EQ(expected_precision, precision);
EXPECT_EQ(expected_scale, scale);
}
} // namespace
TEST(DecimalTest, Basics) {
AssertDecimalFromString("234.23445", Decimal(23423445), 8, 5);
std::string string_value("-23049223942343532412");
Decimal result(string_value);
Decimal expected(static_cast<int64_t>(-230492239423435324));
ASSERT_EQ(result, expected * 100 - 12);
ASSERT_NE(result.high(), 0);
result = Decimal("-23049223942343.532412");
ASSERT_EQ(result, expected * 100 - 12);
ASSERT_NE(result.high(), 0);
}
TEST(DecimalTest, StringStartingWithSign) {
AssertDecimalFromString("+234.567", Decimal(234567), 6, 3);
AssertDecimalFromString("+2342394230592.232349023094",
Decimal("2342394230592232349023094"), 25, 12);
AssertDecimalFromString("-234.567", Decimal("-234567"), 6, 3);
AssertDecimalFromString("-2342394230592.232349023094",
Decimal("-2342394230592232349023094"), 25, 12);
}
TEST(DecimalTest, StringWithLeadingZeros) {
AssertDecimalFromString("0000000000000000000000000000000.234", Decimal(234), 3, 3);
AssertDecimalFromString("0000000000000000000000000000000.23400", Decimal(23400), 5, 5);
AssertDecimalFromString("234.00", Decimal(23400), 5, 2);
AssertDecimalFromString("234.0", Decimal(2340), 4, 1);
AssertDecimalFromString("0000000", Decimal(0), 0, 0);
AssertDecimalFromString("000.0000", Decimal(0), 4, 4);
AssertDecimalFromString(".00000", Decimal(0), 5, 5);
}
TEST(DecimalTest, DecimalWithExponent) {
AssertDecimalFromString("1E1", Decimal(10), 2, 0);
AssertDecimalFromString("234.23445e2", Decimal(23423445), 8, 3);
AssertDecimalFromString("234.23445e-2", Decimal(23423445), 8, 7);
AssertDecimalFromString("234.23445E2", Decimal(23423445), 8, 3);
AssertDecimalFromString("234.23445E-2", Decimal(23423445), 8, 7);
AssertDecimalFromString("1.23E-8", Decimal(123), 3, 10);
}
TEST(DecimalTest, SmallValues) {
struct TestValue {
std::string s;
int64_t expected;
int32_t expected_precision;
int32_t expected_scale;
};
for (const auto& tv : std::vector<TestValue>{
{.s = "12.3", .expected = 123LL, .expected_precision = 3, .expected_scale = 1},
{.s = "0.00123",
.expected = 123LL,
.expected_precision = 5,
.expected_scale = 5},
{.s = "1.23E-8",
.expected = 123LL,
.expected_precision = 3,
.expected_scale = 10},
{.s = "-1.23E-8",
.expected = -123LL,
.expected_precision = 3,
.expected_scale = 10},
{.s = "1.23E+3",
.expected = 1230LL,
.expected_precision = 4,
.expected_scale = 0},
{.s = "-1.23E+3",
.expected = -1230LL,
.expected_precision = 4,
.expected_scale = 0},
{.s = "1.23E+5",
.expected = 123000LL,
.expected_precision = 6,
.expected_scale = 0},
{.s = "1.2345E+7",
.expected = 12345000LL,
.expected_precision = 8,
.expected_scale = 0},
{.s = "1.23e-8",
.expected = 123LL,
.expected_precision = 3,
.expected_scale = 10},
{.s = "-1.23e-8",
.expected = -123LL,
.expected_precision = 3,
.expected_scale = 10},
{.s = "1.23e+3",
.expected = 1230LL,
.expected_precision = 4,
.expected_scale = 0},
{.s = "-1.23e+3",
.expected = -1230LL,
.expected_precision = 4,
.expected_scale = 0},
{.s = "1.23e+5",
.expected = 123000LL,
.expected_precision = 6,
.expected_scale = 0},
{.s = "1.2345e+7",
.expected = 12345000LL,
.expected_precision = 8,
.expected_scale = 0}}) {
AssertDecimalFromString(tv.s, Decimal(tv.expected), tv.expected_precision,
tv.expected_scale);
}
}
TEST(DecimalTest, LargeValues) {
const std::array<std::string, 4> string_values = {
"99999999999999999999999999999999999999", "-99999999999999999999999999999999999999",
"170141183460469231731687303715884105727", // maximum positive value
"-170141183460469231731687303715884105728" // minimum negative value
};
for (const auto& s : string_values) {
const Decimal value(s);
const std::string printed_value = value.ToIntegerString();
EXPECT_EQ(printed_value, s) << "Expected: " << s << ", but got: " << printed_value;
}
}
TEST(DecimalTest, TestStringRoundTrip) {
static constexpr std::array<uint64_t, 11> kTestBits = {
0,
1,
999,
1000,
std::numeric_limits<int32_t>::max(),
(1ull << 31),
std::numeric_limits<uint32_t>::max(),
(1ull << 32),
std::numeric_limits<int64_t>::max(),
(1ull << 63),
std::numeric_limits<uint64_t>::max(),
};
static constexpr std::array<int32_t, 3> kScales = {0, 1, 10};
for (uint64_t high : kTestBits) {
for (uint64_t low : kTestBits) {
Decimal value(high, low);
for (int32_t scale : kScales) {
auto result = value.ToString(scale);
ASSERT_THAT(result, IsOk())
<< "Failed to convert Decimal to string: " << value.ToIntegerString()
<< ", scale: " << scale;
auto round_trip = Decimal::FromString(result.value());
ASSERT_THAT(round_trip, IsOk())
<< "Failed to convert string back to Decimal: " << result.value();
EXPECT_EQ(value, round_trip.value())
<< "Round trip failed for value: " << value.ToIntegerString()
<< ", scale: " << scale;
}
}
}
}
TEST(DecimalTest, FromStringLimits) {
AssertDecimalFromString("1e37", Decimal(542101086242752217ULL, 68739955140067328ULL),
38, 0);
AssertDecimalFromString(
"-1e37", Decimal(17904642987466799398ULL, 18378004118569484288ULL), 38, 0);
AssertDecimalFromString(
"9.87e37", Decimal(5350537721215964381ULL, 15251391175463010304ULL), 38, 0);
AssertDecimalFromString(
"-9.87e37", Decimal(13096206352493587234ULL, 3195352898246541312ULL), 38, 0);
AssertDecimalFromString("12345678901234567890123456789012345678",
Decimal(669260594276348691ULL, 14143994781733811022ULL), 38, 0);
AssertDecimalFromString("-12345678901234567890123456789012345678",
Decimal(17777483479433202924ULL, 4302749291975740594ULL), 38,
0);
// "9..9" (38 times)
const auto dec38times9pos = Decimal(5421010862427522170ULL, 687399551400673279ULL);
// "-9..9" (38 times)
const auto dec38times9neg = Decimal(13025733211282029445ULL, 17759344522308878337ULL);
AssertDecimalFromString("99999999999999999999999999999999999999", dec38times9pos, 38,
0);
AssertDecimalFromString("-99999999999999999999999999999999999999", dec38times9neg, 38,
0);
AssertDecimalFromString("9.9999999999999999999999999999999999999e37", dec38times9pos,
38, 0);
AssertDecimalFromString("-9.9999999999999999999999999999999999999e37", dec38times9neg,
38, 0);
// No exponent, many fractional digits
AssertDecimalFromString("9.9999999999999999999999999999999999999", dec38times9pos, 38,
37);
AssertDecimalFromString("-9.9999999999999999999999999999999999999", dec38times9neg, 38,
37);
AssertDecimalFromString("0.99999999999999999999999999999999999999", dec38times9pos, 38,
38);
AssertDecimalFromString("-0.99999999999999999999999999999999999999", dec38times9neg, 38,
38);
// Negative exponent
AssertDecimalFromString("1e-38", Decimal(0, 1), 1, 38);
AssertDecimalFromString(
"-1e-38", Decimal(18446744073709551615ULL, 18446744073709551615ULL), 1, 38);
AssertDecimalFromString("9.99e-36", Decimal(0, 999), 3, 38);
AssertDecimalFromString(
"-9.99e-36", Decimal(18446744073709551615ULL, 18446744073709550617ULL), 3, 38);
AssertDecimalFromString("987e-38", Decimal(0, 987), 3, 38);
AssertDecimalFromString(
"-987e-38", Decimal(18446744073709551615ULL, 18446744073709550629ULL), 3, 38);
AssertDecimalFromString("99999999999999999999999999999999999999e-37", dec38times9pos,
38, 37);
AssertDecimalFromString("-99999999999999999999999999999999999999e-37", dec38times9neg,
38, 37);
AssertDecimalFromString("99999999999999999999999999999999999999e-38", dec38times9pos,
38, 38);
AssertDecimalFromString("-99999999999999999999999999999999999999e-38", dec38times9neg,
38, 38);
}
TEST(DecimalTest, FromStringInvalid) {
// Empty string
auto result = Decimal::FromString("");
ASSERT_THAT(result, IsError(ErrorKind::kInvalidArgument));
ASSERT_THAT(result, HasErrorMessage("Empty string is not a valid Decimal"));
for (const auto& invalid_string :
std::vector<std::string>{"-", "0.0.0", "0-13-32", "a", "-23092.235-",
"-+23092.235", "+-23092.235", "00a", "1e1a", "0.00123D/3",
"1.23eA8", "1.23E+3A", "-1.23E--5", "1.2345E+++07"}) {
auto result = Decimal::FromString(invalid_string);
ASSERT_THAT(result, IsError(ErrorKind::kInvalidArgument));
ASSERT_THAT(result, HasErrorMessage("Invalid decimal string"));
}
for (const auto& invalid_string :
std::vector<std::string>{"1e39", "-1e39", "9e39", "-9e39", "9.9e40", "-9.9e40"}) {
auto result = Decimal::FromString(invalid_string);
ASSERT_THAT(result, IsError(ErrorKind::kInvalidArgument));
ASSERT_THAT(result, HasErrorMessage("scale must be in the range"));
}
}
TEST(DecimalTest, Division) {
const std::string expected_string_value("-23923094039234029");
const Decimal value(expected_string_value);
const Decimal result(value / 3);
const Decimal expected_value("-7974364679744676");
ASSERT_EQ(expected_value, result);
}
TEST(DecimalTest, ToString) {
struct ToStringCase {
int64_t test_value;
int32_t scale;
const char* expected_string;
};
for (const auto& t : std::vector<ToStringCase>{
{.test_value = 0, .scale = -1, .expected_string = "0E+1"},
{.test_value = 0, .scale = 0, .expected_string = "0"},
{.test_value = 0, .scale = 1, .expected_string = "0.0"},
{.test_value = 0, .scale = 6, .expected_string = "0.000000"},
{.test_value = 2, .scale = 7, .expected_string = "2E-7"},
{.test_value = 2, .scale = -1, .expected_string = "2E+1"},
{.test_value = 2, .scale = 0, .expected_string = "2"},
{.test_value = 2, .scale = 1, .expected_string = "0.2"},
{.test_value = 2, .scale = 6, .expected_string = "0.000002"},
{.test_value = -2, .scale = 7, .expected_string = "-2E-7"},
{.test_value = -2, .scale = 7, .expected_string = "-2E-7"},
{.test_value = -2, .scale = -1, .expected_string = "-2E+1"},
{.test_value = -2, .scale = 0, .expected_string = "-2"},
{.test_value = -2, .scale = 1, .expected_string = "-0.2"},
{.test_value = -2, .scale = 6, .expected_string = "-0.000002"},
{.test_value = -2, .scale = 7, .expected_string = "-2E-7"},
{.test_value = 123, .scale = -3, .expected_string = "1.23E+5"},
{.test_value = 123, .scale = -1, .expected_string = "1.23E+3"},
{.test_value = 123, .scale = 1, .expected_string = "12.3"},
{.test_value = 123, .scale = 0, .expected_string = "123"},
{.test_value = 123, .scale = 5, .expected_string = "0.00123"},
{.test_value = 123, .scale = 8, .expected_string = "0.00000123"},
{.test_value = 123, .scale = 9, .expected_string = "1.23E-7"},
{.test_value = 123, .scale = 10, .expected_string = "1.23E-8"},
{.test_value = -123, .scale = -3, .expected_string = "-1.23E+5"},
{.test_value = -123, .scale = -1, .expected_string = "-1.23E+3"},
{.test_value = -123, .scale = 1, .expected_string = "-12.3"},
{.test_value = -123, .scale = 0, .expected_string = "-123"},
{.test_value = -123, .scale = 5, .expected_string = "-0.00123"},
{.test_value = -123, .scale = 8, .expected_string = "-0.00000123"},
{.test_value = -123, .scale = 9, .expected_string = "-1.23E-7"},
{.test_value = -123, .scale = 10, .expected_string = "-1.23E-8"},
{.test_value = 1000000000, .scale = -3, .expected_string = "1.000000000E+12"},
{.test_value = 1000000000, .scale = -1, .expected_string = "1.000000000E+10"},
{.test_value = 1000000000, .scale = 0, .expected_string = "1000000000"},
{.test_value = 1000000000, .scale = 1, .expected_string = "100000000.0"},
{.test_value = 1000000000, .scale = 5, .expected_string = "10000.00000"},
{.test_value = 1000000000,
.scale = 15,
.expected_string = "0.000001000000000"},
{.test_value = 1000000000, .scale = 16, .expected_string = "1.000000000E-7"},
{.test_value = 1000000000, .scale = 17, .expected_string = "1.000000000E-8"},
{.test_value = -1000000000,
.scale = -3,
.expected_string = "-1.000000000E+12"},
{.test_value = -1000000000,
.scale = -1,
.expected_string = "-1.000000000E+10"},
{.test_value = -1000000000, .scale = 0, .expected_string = "-1000000000"},
{.test_value = -1000000000, .scale = 1, .expected_string = "-100000000.0"},
{.test_value = -1000000000, .scale = 5, .expected_string = "-10000.00000"},
{.test_value = -1000000000,
.scale = 15,
.expected_string = "-0.000001000000000"},
{.test_value = -1000000000, .scale = 16, .expected_string = "-1.000000000E-7"},
{.test_value = -1000000000, .scale = 17, .expected_string = "-1.000000000E-8"},
{.test_value = 1234567890123456789LL,
.scale = -3,
.expected_string = "1.234567890123456789E+21"},
{.test_value = 1234567890123456789LL,
.scale = -1,
.expected_string = "1.234567890123456789E+19"},
{.test_value = 1234567890123456789LL,
.scale = 0,
.expected_string = "1234567890123456789"},
{.test_value = 1234567890123456789LL,
.scale = 1,
.expected_string = "123456789012345678.9"},
{.test_value = 1234567890123456789LL,
.scale = 5,
.expected_string = "12345678901234.56789"},
{.test_value = 1234567890123456789LL,
.scale = 24,
.expected_string = "0.000001234567890123456789"},
{.test_value = 1234567890123456789LL,
.scale = 25,
.expected_string = "1.234567890123456789E-7"},
{.test_value = -1234567890123456789LL,
.scale = -3,
.expected_string = "-1.234567890123456789E+21"},
{.test_value = -1234567890123456789LL,
.scale = -1,
.expected_string = "-1.234567890123456789E+19"},
{.test_value = -1234567890123456789LL,
.scale = 0,
.expected_string = "-1234567890123456789"},
{.test_value = -1234567890123456789LL,
.scale = 1,
.expected_string = "-123456789012345678.9"},
{.test_value = -1234567890123456789LL,
.scale = 5,
.expected_string = "-12345678901234.56789"},
{.test_value = -1234567890123456789LL,
.scale = 24,
.expected_string = "-0.000001234567890123456789"},
{.test_value = -1234567890123456789LL,
.scale = 25,
.expected_string = "-1.234567890123456789E-7"},
}) {
const Decimal value(t.test_value);
auto result = value.ToString(t.scale);
ASSERT_THAT(result, IsOk())
<< "Failed to convert Decimal to string: " << value.ToIntegerString()
<< ", scale: " << t.scale;
EXPECT_EQ(result.value(), t.expected_string)
<< "Expected: " << t.expected_string << ", but got: " << result.value();
}
}
TEST(DecimalTest, FromBigEndian) {
// We test out a variety of scenarios:
//
// * Positive values that are left shifted
// and filled in with the same bit pattern
// * Negated of the positive values
// * Complement of the positive values
//
// For the positive values, we can call FromBigEndian
// with a length that is less than 16, whereas we must
// pass all 16 bytes for the negative and complement.
//
// We use a number of bit patterns to increase the coverage
// of scenarios
constexpr int WidthMinusOne = Decimal::kByteWidth - 1;
for (int32_t start : {1, 15, /* 00001111 */
85, /* 01010101 */
127 /* 01111111 */}) {
Decimal value(start);
for (int ii = 0; ii < Decimal::kByteWidth; ++ii) {
auto native_endian = value.ToBytes();
if constexpr (std::endian::native == std::endian::little) {
// convert to big endian
std::ranges::reverse(native_endian);
}
// Limit the number of bytes we are passing to make
// sure that it works correctly. That's why all of the
// 'start' values don't have a 1 in the most significant
// bit place
auto result =
Decimal::FromBigEndian(native_endian.data() + WidthMinusOne - ii, ii + 1);
ASSERT_THAT(result, IsOk());
const Decimal& decimal = result.value();
EXPECT_EQ(decimal, value);
// Negate it
auto negated = -value;
native_endian = negated.ToBytes();
if constexpr (std::endian::native == std::endian::little) {
// convert to big endian
std::ranges::reverse(native_endian);
}
result = Decimal::FromBigEndian(native_endian.data() + WidthMinusOne - ii, ii + 1);
ASSERT_THAT(result, IsOk());
const Decimal& negated_decimal = result.value();
EXPECT_EQ(negated_decimal, negated);
// Take the complement
auto complement = ~value;
native_endian = complement.ToBytes();
if constexpr (std::endian::native == std::endian::little) {
// convert to big endian
std::ranges::reverse(native_endian);
}
result = Decimal::FromBigEndian(native_endian.data(), Decimal::kByteWidth);
ASSERT_THAT(result, IsOk());
const Decimal& complement_decimal = result.value();
EXPECT_EQ(complement_decimal, complement);
value <<= 2;
value += Decimal(start);
}
}
}
TEST(DecimalTest, FromBigEndianInvalid) {
ASSERT_THAT(Decimal::FromBigEndian(nullptr, -1), IsError(ErrorKind::kInvalidArgument));
ASSERT_THAT(Decimal::FromBigEndian(nullptr, Decimal::kByteWidth + 1),
IsError(ErrorKind::kInvalidArgument));
}
TEST(DecimalTest, ToBigEndian) {
std::vector<int64_t> high_values = {0,
1,
-1,
INT32_MAX,
INT32_MIN,
static_cast<int64_t>(INT32_MAX) + 1,
static_cast<int64_t>(INT32_MIN) - 1,
INT64_MAX,
INT64_MIN};
std::vector<uint64_t> low_values = {0,
1,
255,
UINT32_MAX,
static_cast<uint64_t>(UINT32_MAX) + 1,
static_cast<uint64_t>(UINT32_MAX) + 2,
static_cast<uint64_t>(UINT32_MAX) + 3,
static_cast<uint64_t>(UINT32_MAX) + 4,
static_cast<uint64_t>(UINT32_MAX) + 5,
static_cast<uint64_t>(UINT32_MAX) + 6,
static_cast<uint64_t>(UINT32_MAX) + 7,
static_cast<uint64_t>(UINT32_MAX) + 8,
UINT64_MAX};
for (int64_t high : high_values) {
for (uint64_t low : low_values) {
Decimal decimal(high, low);
auto bytes = decimal.ToBigEndian();
auto result = Decimal::FromBigEndian(bytes.data(), bytes.size());
ASSERT_THAT(result, IsOk());
EXPECT_EQ(result.value(), decimal);
}
}
for (int128_t value : std::vector<int128_t>{-INT64_MAX, -INT32_MAX, -255, -1, 0, 1, 255,
256, INT32_MAX, INT64_MAX}) {
Decimal decimal(value);
auto bytes = decimal.ToBigEndian();
auto result = Decimal::FromBigEndian(bytes.data(), bytes.size());
ASSERT_THAT(result, IsOk());
EXPECT_EQ(result.value(), decimal);
}
}
TEST(DecimalTestFunctionality, Multiply) {
ASSERT_EQ(Decimal(60501), Decimal(301) * Decimal(201));
ASSERT_EQ(Decimal(-60501), Decimal(-301) * Decimal(201));
ASSERT_EQ(Decimal(-60501), Decimal(301) * Decimal(-201));
ASSERT_EQ(Decimal(60501), Decimal(-301) * Decimal(-201));
// Edge cases
for (auto x : std::vector<int128_t>{-INT64_MAX, -INT32_MAX, 0, INT32_MAX, INT64_MAX}) {
for (auto y :
std::vector<int128_t>{-INT32_MAX, -32, -2, -1, 0, 1, 2, 32, INT32_MAX}) {
Decimal decimal_x(x);
Decimal decimal_y(y);
Decimal result = decimal_x * decimal_y;
EXPECT_EQ(Decimal(x * y), result) << " x: " << decimal_x << " y: " << decimal_y;
}
}
}
TEST(DecimalTestFunctionality, Divide) {
ASSERT_EQ(Decimal(66), Decimal(20100) / Decimal(301));
ASSERT_EQ(Decimal(-66), Decimal(-20100) / Decimal(301));
ASSERT_EQ(Decimal(-66), Decimal(20100) / Decimal(-301));
ASSERT_EQ(Decimal(66), Decimal(-20100) / Decimal(-301));
for (auto x : std::vector<int128_t>{-INT64_MAX, -INT32_MAX, 0, INT32_MAX, INT64_MAX}) {
for (auto y : std::vector<int128_t>{-INT32_MAX, -32, -2, -1, 1, 2, 32, INT32_MAX}) {
Decimal decimal_x(x);
Decimal decimal_y(y);
Decimal result = decimal_x / decimal_y;
EXPECT_EQ(Decimal(x / y), result) << " x: " << decimal_x << " y: " << decimal_y;
}
}
}
TEST(DecimalTestFunctionality, Modulo) {
ASSERT_EQ(Decimal(234), Decimal(20100) % Decimal(301));
ASSERT_EQ(Decimal(-234), Decimal(-20100) % Decimal(301));
ASSERT_EQ(Decimal(234), Decimal(20100) % Decimal(-301));
ASSERT_EQ(Decimal(-234), Decimal(-20100) % Decimal(-301));
// Test some edge cases
for (auto x : std::vector<int128_t>{-INT64_MAX, -INT32_MAX, 0, INT32_MAX, INT64_MAX}) {
for (auto y : std::vector<int128_t>{-INT32_MAX, -32, -2, -1, 1, 2, 32, INT32_MAX}) {
Decimal decimal_x(x);
Decimal decimal_y(y);
Decimal result = decimal_x % decimal_y;
EXPECT_EQ(Decimal(x % y), result) << " x: " << decimal_x << " y: " << decimal_y;
}
}
}
TEST(DecimalTestFunctionality, Sign) {
ASSERT_EQ(1, Decimal(999999).Sign());
ASSERT_EQ(-1, Decimal(-999999).Sign());
ASSERT_EQ(1, Decimal(0).Sign());
}
TEST(DecimalTestFunctionality, FitsInPrecision) {
ASSERT_TRUE(Decimal("0").FitsInPrecision(1));
ASSERT_TRUE(Decimal("9").FitsInPrecision(1));
ASSERT_TRUE(Decimal("-9").FitsInPrecision(1));
ASSERT_FALSE(Decimal("10").FitsInPrecision(1));
ASSERT_FALSE(Decimal("-10").FitsInPrecision(1));
ASSERT_TRUE(Decimal("0").FitsInPrecision(2));
ASSERT_TRUE(Decimal("10").FitsInPrecision(2));
ASSERT_TRUE(Decimal("-10").FitsInPrecision(2));
ASSERT_TRUE(Decimal("99").FitsInPrecision(2));
ASSERT_TRUE(Decimal("-99").FitsInPrecision(2));
ASSERT_FALSE(Decimal("100").FitsInPrecision(2));
ASSERT_FALSE(Decimal("-100").FitsInPrecision(2));
std::string max_nines(Decimal::kMaxPrecision, '9');
ASSERT_TRUE(Decimal(max_nines).FitsInPrecision(Decimal::kMaxPrecision));
ASSERT_TRUE(Decimal("-" + max_nines).FitsInPrecision(Decimal::kMaxPrecision));
std::string max_zeros(Decimal::kMaxPrecision, '0');
ASSERT_FALSE(Decimal("1" + max_zeros).FitsInPrecision(Decimal::kMaxPrecision));
ASSERT_FALSE(Decimal("-1" + max_zeros).FitsInPrecision(Decimal::kMaxPrecision));
}
TEST(DecimalTest, LeftShift) {
auto check = [](int128_t x, uint32_t bits) {
auto expected = Decimal(x << bits);
auto actual = Decimal(x) << bits;
ASSERT_EQ(actual.low(), expected.low());
ASSERT_EQ(actual.high(), expected.high());
};
ASSERT_EQ(Decimal("0"), Decimal("0") << 0);
ASSERT_EQ(Decimal("0"), Decimal("0") << 1);
ASSERT_EQ(Decimal("0"), Decimal("0") << 63);
ASSERT_EQ(Decimal("0"), Decimal("0") << 127);
check(123, 0);
check(123, 1);
check(123, 63);
check(123, 64);
check(123, 120);
ASSERT_EQ(Decimal("199999999999998"), Decimal("99999999999999") << 1);
ASSERT_EQ(Decimal("3435973836799965640261632"), Decimal("99999999999999") << 35);
ASSERT_EQ(Decimal("120892581961461708544797985370825293824"), Decimal("99999999999999")
<< 80);
ASSERT_EQ(Decimal("1234567890123456789012"), Decimal("1234567890123456789012") << 0);
ASSERT_EQ(Decimal("2469135780246913578024"), Decimal("1234567890123456789012") << 1);
ASSERT_EQ(Decimal("88959991838777271103427858320412639232"),
Decimal("1234567890123456789012") << 56);
check(-123, 0);
check(-123, 1);
check(-123, 63);
check(-123, 64);
check(-123, 120);
ASSERT_EQ(Decimal("-199999999999998"), Decimal("-99999999999999") << 1);
ASSERT_EQ(Decimal("-3435973836799965640261632"), Decimal("-99999999999999") << 35);
ASSERT_EQ(Decimal("-120892581961461708544797985370825293824"),
Decimal("-99999999999999") << 80);
ASSERT_EQ(Decimal("-1234567890123456789012"), Decimal("-1234567890123456789012") << 0);
ASSERT_EQ(Decimal("-2469135780246913578024"), Decimal("-1234567890123456789012") << 1);
ASSERT_EQ(Decimal("-88959991838777271103427858320412639232"),
Decimal("-1234567890123456789012") << 56);
}
TEST(DecimalTest, RightShift) {
ASSERT_EQ(Decimal("0"), Decimal("0") >> 0);
ASSERT_EQ(Decimal("0"), Decimal("0") >> 1);
ASSERT_EQ(Decimal("0"), Decimal("0") >> 63);
ASSERT_EQ(Decimal("0"), Decimal("0") >> 127);
ASSERT_EQ(Decimal("1"), Decimal("1") >> 0);
ASSERT_EQ(Decimal("0"), Decimal("1") >> 1);
ASSERT_EQ(Decimal("0"), Decimal("1") >> 63);
ASSERT_EQ(Decimal("0"), Decimal("1") >> 127);
ASSERT_EQ(Decimal("-1"), Decimal("-1") >> 0);
ASSERT_EQ(Decimal("-1"), Decimal("-1") >> 1);
ASSERT_EQ(Decimal("-1"), Decimal("-1") >> 63);
ASSERT_EQ(Decimal("-1"), Decimal("-1") >> 127);
ASSERT_EQ(Decimal("1096516"), Decimal("1234567890123456789012") >> 50);
ASSERT_EQ(Decimal("66"), Decimal("1234567890123456789012") >> 64);
ASSERT_EQ(Decimal("2"), Decimal("1234567890123456789012") >> 69);
ASSERT_EQ(Decimal("0"), Decimal("1234567890123456789012") >> 71);
ASSERT_EQ(Decimal("0"), Decimal("1234567890123456789012") >> 127);
ASSERT_EQ(Decimal("-1096517"), Decimal("-1234567890123456789012") >> 50);
ASSERT_EQ(Decimal("-67"), Decimal("-1234567890123456789012") >> 64);
ASSERT_EQ(Decimal("-3"), Decimal("-1234567890123456789012") >> 69);
ASSERT_EQ(Decimal("-1"), Decimal("-1234567890123456789012") >> 71);
ASSERT_EQ(Decimal("-1"), Decimal("-1234567890123456789012") >> 127);
}
TEST(DecimalTest, Negate) {
auto check = [](Decimal pos, Decimal neg) {
EXPECT_EQ(-pos, neg);
EXPECT_EQ(-neg, pos);
};
check(Decimal(0, 0), Decimal(0, 0));
check(Decimal(0, 1), Decimal(-1, 0xFFFFFFFFFFFFFFFFULL));
check(Decimal(0, 2), Decimal(-1, 0xFFFFFFFFFFFFFFFEULL));
check(Decimal(0, 0x8000000000000000ULL), Decimal(-1, 0x8000000000000000ULL));
check(Decimal(0, 0xFFFFFFFFFFFFFFFFULL), Decimal(-1, 1));
check(Decimal(12, 0), Decimal(-12, 0));
check(Decimal(12, 1), Decimal(-13, 0xFFFFFFFFFFFFFFFFULL));
check(Decimal(12, 0xFFFFFFFFFFFFFFFFULL), Decimal(-13, 1));
}
TEST(DecimalTest, Rescale) {
ASSERT_EQ(Decimal(11100), Decimal(111).Rescale(0, 2).value());
ASSERT_EQ(Decimal(111), Decimal(11100).Rescale(2, 0).value());
ASSERT_EQ(Decimal(5), Decimal(500000).Rescale(6, 1).value());
ASSERT_EQ(Decimal(500000), Decimal(5).Rescale(1, 6).value());
ASSERT_THAT(Decimal(5555555).Rescale(6, 1), IsError(ErrorKind::kInvalid));
}
TEST(DecimalTest, Compare) {
// max positive unscaled value
// 10^38 - 1 scale cause overflow
ASSERT_EQ(Decimal::Compare(Decimal("99999999999999999999999999999999999999"),
Decimal("99999999999999999999999999999999999999"), 2, 3),
std::partial_ordering::greater);
// 10^37 - 1 scale no overflow
ASSERT_EQ(Decimal::Compare(Decimal("9999999999999999999999999999999999999"),
Decimal("99999999999999999999999999999999999999"), 2, 3),
std::partial_ordering::less);
// min negative unscaled value
// -10^38 + 1 scale cause overflow
ASSERT_EQ(Decimal::Compare(Decimal("-99999999999999999999999999999999999999"),
Decimal("-99999999999999999999999999999999999999"), 2, 3),
std::partial_ordering::less);
// -10^37 + 1 scale no overflow
ASSERT_EQ(Decimal::Compare(Decimal("-9999999999999999999999999999999999999"),
Decimal("-99999999999999999999999999999999999999"), 2, 3),
std::partial_ordering::greater);
// equal values with different scales
ASSERT_EQ(Decimal::Compare(Decimal("123456789"), Decimal("1234567890"), 2, 3),
std::partial_ordering::equivalent);
ASSERT_EQ(Decimal::Compare(Decimal("-1234567890"), Decimal("-123456789"), 3, 2),
std::partial_ordering::equivalent);
// different values with different scales
ASSERT_EQ(Decimal::Compare(Decimal("123456788"), Decimal("1234567890"), 2, 3),
std::partial_ordering::less);
ASSERT_EQ(Decimal::Compare(Decimal("-1234567890"), Decimal("-123456788"), 2, 3),
std::partial_ordering::less);
// different values with same scales
ASSERT_EQ(Decimal::Compare(Decimal("123456790"), Decimal("123456789"), 2, 2),
std::partial_ordering::greater);
ASSERT_EQ(Decimal::Compare(Decimal("-123456790"), Decimal("-123456789"), 2, 2),
std::partial_ordering::less);
// different signs
ASSERT_EQ(Decimal::Compare(Decimal("123456789"), Decimal("-123456789"), 2, 3),
std::partial_ordering::greater);
ASSERT_EQ(Decimal::Compare(Decimal("-123456789"), Decimal("123456789"), 2, 3),
std::partial_ordering::less);
// zero comparisons
ASSERT_EQ(Decimal::Compare(Decimal("0"), Decimal("0"), 2, 3),
std::partial_ordering::equivalent);
ASSERT_EQ(Decimal::Compare(Decimal("0"), Decimal("123456789"), 2, 3),
std::partial_ordering::less);
ASSERT_EQ(Decimal::Compare(Decimal("-123456789"), Decimal("0"), 2, 3),
std::partial_ordering::less);
}
} // namespace iceberg