Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions test/json/json_decimal_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1299,3 +1299,19 @@ TEST(divisible_by_across_the_whole_exponent_range_is_false) {
sourcemeta::core::Decimal{"3e-2147483648"}};
EXPECT_FALSE(dividend.divisible_by(divisor));
}

// Spelling the same divisor out in full digits rather than as a power of ten
// takes more room to hold, and the verdict cannot turn on which spelling the
// caller reached for, however far the dividend sits above it
TEST(divisible_by_a_wide_exponent_gap_with_the_divisor_in_full_digits) {
const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"1e100"}};
const sourcemeta::core::JSON divisor{
sourcemeta::core::Decimal{"10000000000000000000"}};
EXPECT_TRUE(dividend.divisible_by(divisor));
}

TEST(divisible_by_a_wide_exponent_gap_with_the_divisor_in_exponent_form) {
const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"1e100"}};
const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"1e19"}};
EXPECT_TRUE(dividend.divisible_by(divisor));
}
10 changes: 10 additions & 0 deletions test/numeric/decimal_ibm_dectest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,16 @@ static auto should_skip_file(const std::string &filename) -> bool {
const auto lower{to_lower(filename)};
if (lower.starts_with("dd") || lower.starts_with("dq") ||
lower.starts_with("ds")) {
// The fixed-format files set a precision and a rounding this Decimal
// cannot be asked for, so their arithmetic cannot be judged here. Copying,
// comparing and asking whether two operands share a quantum reach no
// context at all, and those families carry rows no other file does
if (lower.find("copy") != std::string::npos ||
lower.find("compare") != std::string::npos ||

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This also admits ddCompareSig and dqCompareSig, but run_comparison treats comparesig NaN results as unordered boolean comparisons and never checks the expected NaN or invalid_operation. Update the harness to validate CompareSignal results before enabling these files.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At test/numeric/decimal_ibm_dectest.cc, line 630:

<comment>This also admits `ddCompareSig` and `dqCompareSig`, but `run_comparison` treats `comparesig` NaN results as unordered boolean comparisons and never checks the expected NaN or `invalid_operation`. Update the harness to validate CompareSignal results before enabling these files.</comment>

<file context>
@@ -622,6 +622,16 @@ static auto should_skip_file(const std::string &filename) -> bool {
+    // comparing and asking whether two operands share a quantum reach no
+    // context at all, and those families carry rows no other file does
+    if (lower.find("copy") != std::string::npos ||
+        lower.find("compare") != std::string::npos ||
+        lower.find("samequantum") != std::string::npos) {
+      return false;
</file context>

lower.find("samequantum") != std::string::npos) {
return false;
}

return true;
}
return lower.find("randoms") != std::string::npos ||
Expand Down
121 changes: 121 additions & 0 deletions test/numeric/numeric_decimal_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,31 @@ TEST(to_integral_already_integer) {
EXPECT_EQ(value.to_integral(), expected);
}

// Rounding up the last nine of a coefficient carries into a digit that was not
// there before, so the result is one digit wider than what it came from
TEST(to_integral_rounds_up_into_an_extra_digit) {
EXPECT_EQ(sourcemeta::core::Decimal{"9.5"}.to_integral(),
sourcemeta::core::Decimal{10});
EXPECT_EQ(sourcemeta::core::Decimal{"9.9"}.to_integral(),
sourcemeta::core::Decimal{10});
EXPECT_EQ(sourcemeta::core::Decimal{"99.9"}.to_integral(),
sourcemeta::core::Decimal{100});
}

TEST(to_integral_below_the_tie_keeps_the_digit_count) {
EXPECT_EQ(sourcemeta::core::Decimal{"9.4"}.to_integral(),
sourcemeta::core::Decimal{9});
}

TEST(to_integral_rounds_a_negative_up_into_an_extra_digit) {
EXPECT_EQ(sourcemeta::core::Decimal{"-9.5"}.to_integral(),
sourcemeta::core::Decimal{-10});
EXPECT_EQ(sourcemeta::core::Decimal{"-9.9"}.to_integral(),
sourcemeta::core::Decimal{-10});
EXPECT_EQ(sourcemeta::core::Decimal{"-99.9"}.to_integral(),
sourcemeta::core::Decimal{-100});
}

TEST(factory_nan) {
const auto nan_value{sourcemeta::core::Decimal::nan()};
EXPECT_TRUE(nan_value.is_nan());
Expand Down Expand Up @@ -5758,6 +5783,57 @@ TEST(add_negative_scaled_zero_to_positive_zero) {
EXPECT_FALSE(result.is_signed());
}

// A sum of zeros is positive unless both operands were negative, and the
// quantum it keeps is the finer of the two, even with one operand stored at the
// top of the exponent range and the other at the bottom
TEST(add_a_negative_zero_far_below_a_zero_gives_a_positive_zero) {
const sourcemeta::core::Decimal left{"0e2147483647"};
const sourcemeta::core::Decimal right{"-0e-2147483648"};
const sourcemeta::core::Decimal quantum{"0e-2147483648"};
const auto forward{left + right};
const auto reverse{right + left};
EXPECT_TRUE(forward.is_zero());
EXPECT_FALSE(forward.is_signed());
EXPECT_FALSE(forward.is_integer());
EXPECT_TRUE(forward.same_quantum(quantum));
EXPECT_TRUE(reverse.is_zero());
EXPECT_FALSE(reverse.is_signed());
EXPECT_FALSE(reverse.is_integer());
EXPECT_TRUE(reverse.same_quantum(quantum));
}

TEST(add_a_zero_far_below_a_negative_zero_gives_a_positive_zero) {
const sourcemeta::core::Decimal left{"-0e2147483647"};
const sourcemeta::core::Decimal right{"0e-2147483648"};
const sourcemeta::core::Decimal quantum{"0e-2147483648"};
const auto forward{left + right};
const auto reverse{right + left};
EXPECT_TRUE(forward.is_zero());
EXPECT_FALSE(forward.is_signed());
EXPECT_FALSE(forward.is_integer());
EXPECT_TRUE(forward.same_quantum(quantum));
EXPECT_TRUE(reverse.is_zero());
EXPECT_FALSE(reverse.is_signed());
EXPECT_FALSE(reverse.is_integer());
EXPECT_TRUE(reverse.same_quantum(quantum));
}

TEST(add_two_negative_zeros_at_opposite_ends_of_the_exponent_range) {
const sourcemeta::core::Decimal left{"-0e2147483647"};
const sourcemeta::core::Decimal right{"-0e-2147483648"};
const sourcemeta::core::Decimal quantum{"0e-2147483648"};
const auto forward{left + right};
const auto reverse{right + left};
EXPECT_TRUE(forward.is_zero());
EXPECT_TRUE(forward.is_signed());
EXPECT_FALSE(forward.is_integer());
EXPECT_TRUE(forward.same_quantum(quantum));
EXPECT_TRUE(reverse.is_zero());
EXPECT_TRUE(reverse.is_signed());
EXPECT_FALSE(reverse.is_integer());
EXPECT_TRUE(reverse.same_quantum(quantum));
}

TEST(add_opposite_signs_gives_positive_zero) {
const sourcemeta::core::Decimal left{-5};
const sourcemeta::core::Decimal right{5};
Expand Down Expand Up @@ -6491,6 +6567,51 @@ TEST(to_integral_of_a_big_value_at_the_bottom_of_the_exponent_range_is_zero) {
EXPECT_EQ(result.to_string(), "0");
}

// A decimal point that sits past every digit the coefficient holds leaves no
// digit on the integer side to round towards, and the zero that comes out of
// it keeps the sign it went in with
TEST(to_integral_of_a_zero_scaled_below_the_integer_boundary_is_zero) {
const sourcemeta::core::Decimal value{"0e-18"};
const auto result{value.to_integral()};
EXPECT_TRUE(result.is_zero());
EXPECT_FALSE(result.is_signed());
EXPECT_EQ(result.to_string(), "0");
}

TEST(to_integral_of_a_negative_zero_scaled_down_keeps_its_sign) {
const sourcemeta::core::Decimal value{"-0e-36"};
const auto result{value.to_integral()};
EXPECT_TRUE(result.is_zero());
EXPECT_TRUE(result.is_signed());
EXPECT_EQ(result.to_string(), "-0");
}

TEST(to_integral_of_a_value_below_the_integer_boundary_is_zero) {
const sourcemeta::core::Decimal value{"600000000000000000e-36"};
const auto result{value.to_integral()};
EXPECT_TRUE(result.is_zero());
EXPECT_FALSE(result.is_signed());
EXPECT_EQ(result.to_string(), "0");
}

TEST(to_integral_of_a_negative_below_the_integer_boundary_keeps_its_sign) {
const sourcemeta::core::Decimal value{"-600000000000000000e-36"};
const auto result{value.to_integral()};
EXPECT_TRUE(result.is_zero());
EXPECT_TRUE(result.is_signed());
EXPECT_EQ(result.to_string(), "-0");
}

TEST(to_integral_below_the_integer_boundary_matches_the_exponent_form) {
const sourcemeta::core::Decimal full{"600000000000000000e-36"};
const sourcemeta::core::Decimal exponential{"6e-19"};
const auto result{exponential.to_integral()};
EXPECT_EQ(full.to_integral(), result);
EXPECT_TRUE(result.is_zero());
EXPECT_FALSE(result.is_signed());
EXPECT_EQ(result.to_string(), "0");
}

TEST(dividing_minus_one_by_seven_rounds_half_even) {
const sourcemeta::core::Decimal dividend{-1};
const sourcemeta::core::Decimal divisor{7};
Expand Down
Loading