diff --git a/src/core/json/json_value.cc b/src/core/json/json_value.cc index 0feab582a..916884a29 100644 --- a/src/core/json/json_value.cc +++ b/src/core/json/json_value.cc @@ -543,8 +543,13 @@ auto JSON::operator-=(const JSON &substractive) -> JSON & { const auto division{dividend_value / divisor_value}; Real integral = 0; - return !std::isinf(division) && !std::isnan(division) && - std::modf(division, &integral) == 0.0; + if (!std::isinf(division) && !std::isnan(division) && + std::modf(division, &integral) == 0.0) { + return true; + } + + return Decimal::strict_from(dividend_value) + .divisible_by(Decimal::strict_from(divisor_value)); } if (this->is_decimal() && divisor.is_decimal()) { @@ -557,8 +562,8 @@ auto JSON::operator-=(const JSON &substractive) -> JSON & { return this->to_decimal().divisible_by(divisor_decimal); } - const Decimal divisor_decimal{divisor.to_real()}; - return this->to_decimal().divisible_by(divisor_decimal); + return this->to_decimal().divisible_by( + Decimal::strict_from(divisor.to_real())); } if (this->is_integer()) { @@ -566,8 +571,8 @@ auto JSON::operator-=(const JSON &substractive) -> JSON & { return dividend_decimal.divisible_by(divisor.to_decimal()); } - const Decimal dividend_decimal{this->to_real()}; - return dividend_decimal.divisible_by(divisor.to_decimal()); + return Decimal::strict_from(this->to_real()) + .divisible_by(divisor.to_decimal()); } [[nodiscard]] auto diff --git a/src/lang/numeric/decimal.cc b/src/lang/numeric/decimal.cc index 2eeb8cf20..6db7c0a20 100644 --- a/src/lang/numeric/decimal.cc +++ b/src/lang/numeric/decimal.cc @@ -5,7 +5,9 @@ #include // std::array #include // assert +#include // std::to_chars #include // std::isfinite +#include // std::size_t #include // std::strlen #include // std::setprecision #include // std::numeric_limits @@ -506,6 +508,15 @@ auto Decimal::negative_infinity() -> Decimal { return result; } +auto Decimal::strict_from(const double value) -> Decimal { + std::array buffer{}; + const auto result{ + std::to_chars(buffer.data(), buffer.data() + buffer.size(), value)}; + assert(result.ec == std::errc{}); + return Decimal{std::string_view{ + buffer.data(), static_cast(result.ptr - buffer.data())}}; +} + auto Decimal::to_scientific_string() const -> std::string { std::string result; diff --git a/src/lang/numeric/include/sourcemeta/core/numeric_decimal.h b/src/lang/numeric/include/sourcemeta/core/numeric_decimal.h index b07f83ccb..aefc78d73 100644 --- a/src/lang/numeric/include/sourcemeta/core/numeric_decimal.h +++ b/src/lang/numeric/include/sourcemeta/core/numeric_decimal.h @@ -77,6 +77,10 @@ class SOURCEMETA_CORE_NUMERIC_EXPORT Decimal { /// Create a signaling NaN value with an optional payload [[nodiscard]] static auto snan(std::uint64_t payload = 0) -> Decimal; + /// Create a decimal from a double by converting through its shortest + /// round-trip string representation, avoiding IEEE 754 precision artifacts + [[nodiscard]] static auto strict_from(double value) -> Decimal; + /// Create a positive infinity value [[nodiscard]] static auto infinity() -> Decimal; diff --git a/test/json/json_decimal_test.cc b/test/json/json_decimal_test.cc index 0559fb201..8c7448d65 100644 --- a/test/json/json_decimal_test.cc +++ b/test/json/json_decimal_test.cc @@ -878,6 +878,240 @@ TEST(JSON_decimal, divisible_by_mixed_scale_decimal_real_divisible_true) { EXPECT_TRUE(dividend.divisible_by(divisor)); } +TEST(JSON_decimal, divisible_by_decimal_decimal_0_01_true) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"1280.32"}}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.01"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_decimal_0_01_false) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"1280.325"}}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.01"}}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_decimal_0_1_true) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"100.3"}}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.1"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_decimal_0_1_false) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"100.35"}}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.1"}}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_decimal_0_001_true) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"25.123"}}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.001"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_decimal_0_001_false) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"25.1235"}}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.001"}}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_decimal_0_0001_true) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"99.9999"}}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.0001"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_decimal_0_0001_false) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"99.99995"}}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.0001"}}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_real_0_01_true) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"1280.32"}}; + const sourcemeta::core::JSON divisor{0.01}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_real_0_01_false) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"1280.325"}}; + const sourcemeta::core::JSON divisor{0.01}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_real_0_1_true) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"100.3"}}; + const sourcemeta::core::JSON divisor{0.1}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_real_0_1_false) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"100.35"}}; + const sourcemeta::core::JSON divisor{0.1}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_real_0_001_true) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"25.123"}}; + const sourcemeta::core::JSON divisor{0.001}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_real_0_001_false) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"25.1235"}}; + const sourcemeta::core::JSON divisor{0.001}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_real_0_0001_true) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"99.9999"}}; + const sourcemeta::core::JSON divisor{0.0001}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_real_0_0001_false) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"99.99995"}}; + const sourcemeta::core::JSON divisor{0.0001}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_real_decimal_0_01_true) { + const sourcemeta::core::JSON dividend{1280.32}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.01"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_real_decimal_0_01_false) { + const sourcemeta::core::JSON dividend{1280.325}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.01"}}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_real_decimal_0_1_true) { + const sourcemeta::core::JSON dividend{100.3}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.1"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_real_decimal_0_1_false) { + const sourcemeta::core::JSON dividend{100.35}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.1"}}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_real_decimal_0_001_true) { + const sourcemeta::core::JSON dividend{25.123}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.001"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_real_decimal_0_001_false) { + const sourcemeta::core::JSON dividend{25.1235}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.001"}}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_real_decimal_0_0001_true) { + const sourcemeta::core::JSON dividend{99.9999}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.0001"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_real_decimal_0_0001_false) { + const sourcemeta::core::JSON dividend{99.99995}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.0001"}}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_integer_decimal_0_01_true) { + const sourcemeta::core::JSON dividend{100}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.01"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_integer_decimal_0_1_true) { + const sourcemeta::core::JSON dividend{10}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.1"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_integer_decimal_0_001_true) { + const sourcemeta::core::JSON dividend{1}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.001"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_integer_decimal_0_0001_true) { + const sourcemeta::core::JSON dividend{1}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.0001"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_negative_decimal_decimal_0_01_true) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"-1280.32"}}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.01"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_negative_decimal_real_0_01_true) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"-1280.32"}}; + const sourcemeta::core::JSON divisor{0.01}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_negative_real_decimal_0_01_true) { + const sourcemeta::core::JSON dividend{-1280.32}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.01"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_negative_integer_decimal_0_01_true) { + const sourcemeta::core::JSON dividend{-100}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.01"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_decimal_0_3_true) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"0.9"}}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.3"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_decimal_0_3_false) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"1.0"}}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.3"}}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_decimal_0_7_true) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"2.1"}}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.7"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_decimal_decimal_0_7_false) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"2.0"}}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.7"}}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_large_decimal_decimal_0_01_true) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"999999.99"}}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.01"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_large_decimal_real_0_01_true) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"999999.99"}}; + const sourcemeta::core::JSON divisor{0.01}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_decimal, divisible_by_large_decimal_decimal_0_001_true) { + const sourcemeta::core::JSON dividend{sourcemeta::core::Decimal{"12345.678"}}; + const sourcemeta::core::JSON divisor{sourcemeta::core::Decimal{"0.001"}}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + TEST(JSON_decimal, fast_hash_positive) { const sourcemeta::core::JSON document{sourcemeta::core::Decimal{"3.14"}}; EXPECT_EQ(document.fast_hash(), 8); diff --git a/test/json/json_number_test.cc b/test/json/json_number_test.cc index ed13169d8..acb23c037 100644 --- a/test/json/json_number_test.cc +++ b/test/json/json_number_test.cc @@ -354,6 +354,228 @@ TEST(JSON_number, divisible_by_float_overflow_integer_1_0) { EXPECT_TRUE(document.divisible_by(divisor)); } +TEST(JSON_number, divisible_by_real_0_1_true_1) { + const sourcemeta::core::JSON dividend{3.5}; + const sourcemeta::core::JSON divisor{0.1}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_1_true_2) { + const sourcemeta::core::JSON dividend{10.0}; + const sourcemeta::core::JSON divisor{0.1}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_1_true_3) { + const sourcemeta::core::JSON dividend{100.3}; + const sourcemeta::core::JSON divisor{0.1}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_1_true_4) { + const sourcemeta::core::JSON dividend{0.5}; + const sourcemeta::core::JSON divisor{0.1}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_1_false) { + const sourcemeta::core::JSON dividend{3.55}; + const sourcemeta::core::JSON divisor{0.1}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_01_true_1) { + const sourcemeta::core::JSON dividend{1280.32}; + const sourcemeta::core::JSON divisor{0.01}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_01_true_2) { + const sourcemeta::core::JSON dividend{99.99}; + const sourcemeta::core::JSON divisor{0.01}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_01_true_3) { + const sourcemeta::core::JSON dividend{0.01}; + const sourcemeta::core::JSON divisor{0.01}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_01_true_4) { + const sourcemeta::core::JSON dividend{0.5}; + const sourcemeta::core::JSON divisor{0.01}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_01_true_5) { + const sourcemeta::core::JSON dividend{1.0}; + const sourcemeta::core::JSON divisor{0.01}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_01_false) { + const sourcemeta::core::JSON dividend{1.005}; + const sourcemeta::core::JSON divisor{0.01}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_001_true_1) { + const sourcemeta::core::JSON dividend{1.001}; + const sourcemeta::core::JSON divisor{0.001}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_001_true_2) { + const sourcemeta::core::JSON dividend{0.5}; + const sourcemeta::core::JSON divisor{0.001}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_001_true_3) { + const sourcemeta::core::JSON dividend{25.123}; + const sourcemeta::core::JSON divisor{0.001}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_001_false) { + const sourcemeta::core::JSON dividend{1.0005}; + const sourcemeta::core::JSON divisor{0.001}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_0001_true_1) { + const sourcemeta::core::JSON dividend{1.0001}; + const sourcemeta::core::JSON divisor{0.0001}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_0001_true_2) { + const sourcemeta::core::JSON dividend{99.9999}; + const sourcemeta::core::JSON divisor{0.0001}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_0001_false) { + const sourcemeta::core::JSON dividend{1.00005}; + const sourcemeta::core::JSON divisor{0.0001}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_integer_real_0_1_true) { + const sourcemeta::core::JSON dividend{10}; + const sourcemeta::core::JSON divisor{0.1}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_integer_real_0_1_true_2) { + const sourcemeta::core::JSON dividend{1}; + const sourcemeta::core::JSON divisor{0.1}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_integer_real_0_01_true) { + const sourcemeta::core::JSON dividend{1}; + const sourcemeta::core::JSON divisor{0.01}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_integer_real_0_01_true_2) { + const sourcemeta::core::JSON dividend{100}; + const sourcemeta::core::JSON divisor{0.01}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_integer_real_0_001_true) { + const sourcemeta::core::JSON dividend{1}; + const sourcemeta::core::JSON divisor{0.001}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_integer_real_0_0001_true) { + const sourcemeta::core::JSON dividend{1}; + const sourcemeta::core::JSON divisor{0.0001}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_3_true) { + const sourcemeta::core::JSON dividend{0.9}; + const sourcemeta::core::JSON divisor{0.3}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_3_false) { + const sourcemeta::core::JSON dividend{1.0}; + const sourcemeta::core::JSON divisor{0.3}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_7_true) { + const sourcemeta::core::JSON dividend{2.1}; + const sourcemeta::core::JSON divisor{0.7}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_real_0_7_false) { + const sourcemeta::core::JSON dividend{2.0}; + const sourcemeta::core::JSON divisor{0.7}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_negative_real_0_01_true) { + const sourcemeta::core::JSON dividend{-1280.32}; + const sourcemeta::core::JSON divisor{0.01}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_negative_real_0_1_true) { + const sourcemeta::core::JSON dividend{-5.5}; + const sourcemeta::core::JSON divisor{0.1}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_negative_real_0_001_true) { + const sourcemeta::core::JSON dividend{-3.141}; + const sourcemeta::core::JSON divisor{0.001}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_negative_real_0_01_false) { + const sourcemeta::core::JSON dividend{-1.005}; + const sourcemeta::core::JSON divisor{0.01}; + EXPECT_FALSE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_large_real_0_01_true) { + const sourcemeta::core::JSON dividend{999999.99}; + const sourcemeta::core::JSON divisor{0.01}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_large_real_0_001_true) { + const sourcemeta::core::JSON dividend{12345.678}; + const sourcemeta::core::JSON divisor{0.001}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_large_real_0_1_true) { + const sourcemeta::core::JSON dividend{99999.9}; + const sourcemeta::core::JSON divisor{0.1}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_negative_integer_real_0_1_true) { + const sourcemeta::core::JSON dividend{-10}; + const sourcemeta::core::JSON divisor{0.1}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + +TEST(JSON_number, divisible_by_negative_integer_real_0_01_true) { + const sourcemeta::core::JSON dividend{-5}; + const sourcemeta::core::JSON divisor{0.01}; + EXPECT_TRUE(dividend.divisible_by(divisor)); +} + TEST(JSON_number, as_real_real) { const sourcemeta::core::JSON document{4.7}; EXPECT_DOUBLE_EQ(document.as_real(), 4.7); diff --git a/test/numeric/numeric_decimal_test.cc b/test/numeric/numeric_decimal_test.cc index 1342b0481..3d927ff26 100644 --- a/test/numeric/numeric_decimal_test.cc +++ b/test/numeric/numeric_decimal_test.cc @@ -3503,3 +3503,68 @@ TEST(Numeric_decimal, divide_integer_with_decimals) { const sourcemeta::core::Decimal right{"3.1"}; EXPECT_EQ(left.divide_integer(right), sourcemeta::core::Decimal{3}); } + +TEST(Numeric_decimal, strict_from_positive_integer) { + const auto result{sourcemeta::core::Decimal::strict_from(5.0)}; + EXPECT_EQ(result, sourcemeta::core::Decimal{5}); +} + +TEST(Numeric_decimal, strict_from_negative_integer) { + const auto result{sourcemeta::core::Decimal::strict_from(-3.0)}; + EXPECT_EQ(result, sourcemeta::core::Decimal{-3}); +} + +TEST(Numeric_decimal, strict_from_zero) { + const auto result{sourcemeta::core::Decimal::strict_from(0.0)}; + EXPECT_EQ(result, sourcemeta::core::Decimal{0}); +} + +TEST(Numeric_decimal, strict_from_positive_fractional) { + const auto result{sourcemeta::core::Decimal::strict_from(1.5)}; + EXPECT_EQ(result, sourcemeta::core::Decimal{"1.5"}); +} + +TEST(Numeric_decimal, strict_from_negative_fractional) { + const auto result{sourcemeta::core::Decimal::strict_from(-2.75)}; + EXPECT_EQ(result, sourcemeta::core::Decimal{"-2.75"}); +} + +TEST(Numeric_decimal, strict_from_0_1) { + const auto result{sourcemeta::core::Decimal::strict_from(0.1)}; + EXPECT_EQ(result, sourcemeta::core::Decimal{"0.1"}); +} + +TEST(Numeric_decimal, strict_from_0_01) { + const auto result{sourcemeta::core::Decimal::strict_from(0.01)}; + EXPECT_EQ(result, sourcemeta::core::Decimal{"0.01"}); +} + +TEST(Numeric_decimal, strict_from_0_001) { + const auto result{sourcemeta::core::Decimal::strict_from(0.001)}; + EXPECT_EQ(result, sourcemeta::core::Decimal{"0.001"}); +} + +TEST(Numeric_decimal, strict_from_0_0001) { + const auto result{sourcemeta::core::Decimal::strict_from(0.0001)}; + EXPECT_EQ(result, sourcemeta::core::Decimal{"0.0001"}); +} + +TEST(Numeric_decimal, strict_from_1280_32) { + const auto result{sourcemeta::core::Decimal::strict_from(1280.32)}; + EXPECT_EQ(result, sourcemeta::core::Decimal{"1280.32"}); +} + +TEST(Numeric_decimal, strict_from_99_99) { + const auto result{sourcemeta::core::Decimal::strict_from(99.99)}; + EXPECT_EQ(result, sourcemeta::core::Decimal{"99.99"}); +} + +TEST(Numeric_decimal, strict_from_large_value) { + const auto result{sourcemeta::core::Decimal::strict_from(999999.99)}; + EXPECT_EQ(result, sourcemeta::core::Decimal{"999999.99"}); +} + +TEST(Numeric_decimal, strict_from_negative_1280_32) { + const auto result{sourcemeta::core::Decimal::strict_from(-1280.32)}; + EXPECT_EQ(result, sourcemeta::core::Decimal{"-1280.32"}); +}