|
|
@@ -9,13 +9,8 @@
|
|
|
|
|
|
#include "bigdecimal.h"
|
|
|
|
|
|
-TEST(BigDecimalTest, ConstructFromStringIsSameValueAsFromInt) {
|
|
|
- EXPECT_THAT(math::bigdecimal("1000000"),
|
|
|
- math::bigdecimal(1000000));
|
|
|
-}
|
|
|
-
|
|
|
TEST(BigDecimalTest, ConstructIntegerAsDecimal) {
|
|
|
- EXPECT_THAT(math::bigdecimal("1000.00").to_string(), "1000.00");
|
|
|
+ EXPECT_THAT(math::bigdecimal(1000, 2).to_string(), "1000.00");
|
|
|
}
|
|
|
|
|
|
TEST(BigDecimalTest, ConstructDecimal) {
|
|
|
@@ -23,14 +18,21 @@ TEST(BigDecimalTest, ConstructDecimal) {
|
|
|
EXPECT_THAT(math::bigdecimal("1000.01").to_string(), "1000.01");
|
|
|
}
|
|
|
|
|
|
-TEST(BigDecimalTest, RescaleHigherAddsDigits) {
|
|
|
+TEST(BigDecimalTest, RescalePositiveAddsDecimalPlaces) {
|
|
|
math::bigdecimal dec(100);
|
|
|
EXPECT_THAT(dec.scale(), 0);
|
|
|
dec.rescale(2);
|
|
|
EXPECT_THAT(dec.to_string(), "100.00");
|
|
|
}
|
|
|
|
|
|
-TEST(BigDecimalTest, RescaleLowerCanBeLossy) {
|
|
|
+TEST(BigDecimalTest, RescaleNegativeDropsLowerOrderElements) {
|
|
|
+ math::bigdecimal dec(111);
|
|
|
+ EXPECT_THAT(dec.scale(), 0);
|
|
|
+ dec.rescale(-2);
|
|
|
+ EXPECT_THAT(dec.to_string(), "100");
|
|
|
+}
|
|
|
+
|
|
|
+TEST(BigDecimalTest, RescaleDownPermanantlyDropsLowOrderElements) {
|
|
|
math::bigdecimal dec("100.10");
|
|
|
EXPECT_THAT(dec.scale(), 2);
|
|
|
dec.rescale(0);
|
|
|
@@ -38,14 +40,15 @@ TEST(BigDecimalTest, RescaleLowerCanBeLossy) {
|
|
|
EXPECT_THAT(dec.to_string(), "100.00");
|
|
|
}
|
|
|
|
|
|
-TEST(BigDecimalTest, NegativeScaleLosesLowerDigits) {
|
|
|
+TEST(BigDecimalTest, ScaleCtorCanBeLossy) {
|
|
|
math::bigdecimal dec("123", -2);
|
|
|
EXPECT_THAT(dec.to_string(), "100");
|
|
|
}
|
|
|
|
|
|
-TEST(BigDecimalTest, ConstructWithScaleEqualsWithout) {
|
|
|
+TEST(BigDecimalTest, NonLossyScaleCtorEqualsNoScaleCtor) {
|
|
|
math::bigdecimal scl(100, -2);
|
|
|
- EXPECT_THAT(scl.to_string(), "100");
|
|
|
+ math::bigdecimal nc(100);
|
|
|
+ EXPECT_THAT(scl, nc);
|
|
|
}
|
|
|
|
|
|
TEST(BigDecimalTest, ScaleBelowNegSegDoesntLoseAnything) {
|
|
|
@@ -53,102 +56,81 @@ TEST(BigDecimalTest, ScaleBelowNegSegDoesntLoseAnything) {
|
|
|
EXPECT_THAT(dec.to_string(), "1000000000");
|
|
|
}
|
|
|
|
|
|
-TEST(BigDecimalTest, AddTwoDecimalsSameScale) {
|
|
|
+TEST(BigDecimalTest, AddingEqualScalesDoesNotAddTrailingDigitsOrLoseData) {
|
|
|
math::bigdecimal a("1000.10");
|
|
|
math::bigdecimal b("1000.01");
|
|
|
EXPECT_THAT(a.scale(), b.scale());
|
|
|
EXPECT_THAT((a+b).to_string(), "2000.11");
|
|
|
}
|
|
|
|
|
|
-TEST(BigDecimalTest, AddTwoDecimalsDifferentScalesUsesHigherScale) {
|
|
|
+TEST(BigDecimalTest, AdditionNormalizeScaleToHighest) {
|
|
|
math::bigdecimal a("1000.10");
|
|
|
math::bigdecimal b("1000");
|
|
|
EXPECT_THAT(a.scale(), testing::Gt(b.scale()));
|
|
|
- EXPECT_THAT((a+b).to_string(), "2000.10");
|
|
|
+ EXPECT_THAT((a+b).scale(), a.scale());
|
|
|
}
|
|
|
|
|
|
-TEST(BigDecimalTest, AddNumberWithInversePreservesScale) {
|
|
|
+TEST(BigDecimalTest, InverseOfNumberCarriesSameScale) {
|
|
|
math::bigdecimal a("1.001");
|
|
|
- EXPECT_THAT((a+(-a)).to_string(), "0.000");
|
|
|
+ EXPECT_THAT(a.scale(), (-a).scale());
|
|
|
}
|
|
|
|
|
|
-TEST(BigDecimalTest, SubtractTwoDecimalsDifferentScalesUsesHigherScale) {
|
|
|
+TEST(BigDecimalTest, SubtractionNormalizeScaleToHighest) {
|
|
|
math::bigdecimal a("900.10");
|
|
|
math::bigdecimal b("1000");
|
|
|
EXPECT_THAT(a.scale(), testing::Gt(b.scale()));
|
|
|
- EXPECT_THAT((a-b).to_string(), "-99.90");
|
|
|
- EXPECT_THAT((b-a).to_string(), "99.90");
|
|
|
-}
|
|
|
-
|
|
|
-TEST(BigDecimalTest, MultiplicationIncreasesScale) {
|
|
|
- math::bigdecimal bd("1.1");
|
|
|
- math::bigdecimal out = bd * bd;
|
|
|
- EXPECT_THAT(out.scale(), bd.scale() + bd.scale());
|
|
|
- EXPECT_THAT(out.to_string(), "1.21");
|
|
|
-}
|
|
|
-
|
|
|
-TEST(BigDecimalTest, MultiplicationNegativeScaleCountersPositiveScale) {
|
|
|
- math::bigdecimal a("0.01");
|
|
|
- math::bigdecimal b("100", -2);
|
|
|
- EXPECT_THAT((a*b).to_string(), "1");
|
|
|
-}
|
|
|
-
|
|
|
-TEST(BigDecimalTest, MultiplicationCreateNewOffsetCell) {
|
|
|
- math::bigdecimal C(1, 5);
|
|
|
- EXPECT_THAT((C*C).to_string(), "1.0000000000");
|
|
|
-}
|
|
|
-
|
|
|
-TEST(BigDecimalTest, MultiplicationNegativeToPositiveScale) {
|
|
|
- math::bigdecimal C(1, 5);
|
|
|
- math::bigdecimal D(100, -2);
|
|
|
- EXPECT_THAT((D*C).to_string(), "100.000");
|
|
|
-}
|
|
|
-
|
|
|
-TEST(BigDecimalTest, MultiplicationFromDroppedToNonDropScale) {
|
|
|
- math::bigdecimal C(1, 5);
|
|
|
- math::bigdecimal E(1000000000, -9);
|
|
|
- EXPECT_THAT((E*C).to_string(), "1000000000");
|
|
|
-}
|
|
|
-
|
|
|
-TEST(BigDecimalTest, MultiplicationFromDroppedToLowerScale) {
|
|
|
- math::bigdecimal D(100, -2);
|
|
|
- math::bigdecimal E(1000000000, -9);
|
|
|
- EXPECT_THAT((E*D).to_string(), "100000000000");
|
|
|
-}
|
|
|
-
|
|
|
-TEST(BigDecimalTest, MultiplicationFromNonDropToDroppedScale) {
|
|
|
- math::bigdecimal F(10000, -4);
|
|
|
- math::bigdecimal G(100000, -5);
|
|
|
- EXPECT_THAT((G*F).to_string(), "1000000000");
|
|
|
-}
|
|
|
-
|
|
|
-TEST(BigDecimalTest, DivideHigherScaleByLowerScaleGainsDigits) {
|
|
|
- math::bigdecimal a("1", 1);
|
|
|
- math::bigdecimal b("1", 0);
|
|
|
- EXPECT_THAT((a/b).to_string(), "1.0");
|
|
|
-}
|
|
|
-
|
|
|
-TEST(BigDecimalTest, DivideLowerScaleByHigherScaleLosesDigits) {
|
|
|
- math::bigdecimal a("1", 0);
|
|
|
- math::bigdecimal b("1", 1);
|
|
|
- EXPECT_THAT((a/b).to_string(), "0");
|
|
|
-}
|
|
|
-
|
|
|
-TEST(BigDecimalTest, DivideRemainderLostWhenLackingScale) {
|
|
|
- math::bigdecimal a("1" , 0);
|
|
|
- math::bigdecimal c("10", 0);
|
|
|
- EXPECT_THAT((a/c).to_string(), "0");
|
|
|
-}
|
|
|
-
|
|
|
-TEST(BigDecimalTest, DivideRemainderKeptWithScale) {
|
|
|
- math::bigdecimal a("1" , 1);
|
|
|
- math::bigdecimal d("10", 0);
|
|
|
- EXPECT_THAT((a/d).to_string(), "0.1");
|
|
|
-}
|
|
|
-
|
|
|
-TEST(BigDecimalTest, NewDivisionScaleIsDifferenceOfComponentScales) {
|
|
|
- math::bigdecimal b("1" , 1);
|
|
|
- math::bigdecimal d("10", -1);
|
|
|
- EXPECT_THAT((b/d).scale(), b.scale() - d.scale());
|
|
|
- EXPECT_THAT((b/d).to_string(), "0.10");
|
|
|
-}
|
|
|
+ EXPECT_THAT((a-b).scale(), a.scale());
|
|
|
+ EXPECT_THAT((b-a).scale(), a.scale());
|
|
|
+}
|
|
|
+
|
|
|
+struct ArithTuple { math::bigdecimal lhs, rhs; std::string expected; };
|
|
|
+void PrintTo(ArithTuple const & tup, std::ostream * out) {
|
|
|
+ (*out) << "{ lhs = " << tup.lhs.to_string() << "(" << tup.lhs.scale() << "), rhs = " << tup.rhs.to_string() << "(" << tup.rhs.scale() << ") }";
|
|
|
+}
|
|
|
+
|
|
|
+class MultiplicationScaleTest : public testing::TestWithParam<ArithTuple> {};
|
|
|
+
|
|
|
+TEST_P(MultiplicationScaleTest, ScaleIsSumOfOperAndMultipScales) {
|
|
|
+ auto tup = GetParam();
|
|
|
+ auto out = tup.lhs * tup.rhs;
|
|
|
+ EXPECT_THAT(out.scale(), tup.lhs.scale() + tup.rhs.scale());
|
|
|
+ EXPECT_THAT(out.to_string(), tup.expected);
|
|
|
+}
|
|
|
+
|
|
|
+class DivisionScaleTest : public testing::TestWithParam<ArithTuple> {};
|
|
|
+
|
|
|
+TEST_P(DivisionScaleTest, ScaleIsDifferenceOfNumAndDenomScales) {
|
|
|
+ auto tup = GetParam();
|
|
|
+ auto out = tup.lhs / tup.rhs;
|
|
|
+ EXPECT_THAT(out.scale(), tup.lhs.scale() - tup.rhs.scale());
|
|
|
+ EXPECT_THAT(out.to_string(), tup.expected);
|
|
|
+}
|
|
|
+
|
|
|
+#pragma clang diagnostic push
|
|
|
+#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
|
|
|
+INSTANTIATE_TEST_CASE_P(BigDecimal, MultiplicationScaleTest,
|
|
|
+ testing::Values(ArithTuple{{1, 1}, { 1, 0}, "1.0"},
|
|
|
+ ArithTuple{{1, 0}, { 1, 1}, "1.0"},
|
|
|
+ ArithTuple{{1, 0}, {10, 0}, "10" },
|
|
|
+ ArithTuple{{1, 1}, {10, 0}, "10.0"},
|
|
|
+ ArithTuple{{1, 1}, {10, -1}, "10", },
|
|
|
+ ArithTuple{{"1.1"}, {"1.1"}, "1.21"},
|
|
|
+ ArithTuple{{"0.01"}, {100, -2}, "1"},
|
|
|
+ ArithTuple{{1, 5}, {1, 5}, "1.0000000000"},
|
|
|
+ ArithTuple{{1, 5}, {1000000000, -9}, "1000000000"},
|
|
|
+ ArithTuple{{100, -2}, {1000000000, -9}, "100000000000"},
|
|
|
+ ArithTuple{{10000, -4}, {100000, -5}, "1000000000"}));
|
|
|
+
|
|
|
+INSTANTIATE_TEST_CASE_P(BigDecimal, DivisionScaleTest,
|
|
|
+ testing::Values(ArithTuple{{1, 1}, { 1, 0}, "1.0" },
|
|
|
+ ArithTuple{{1, 0}, { 1, 1}, "0" },
|
|
|
+ ArithTuple{{1, 0}, {10, 0}, "0" },
|
|
|
+ ArithTuple{{1, 1}, {10, 0}, "0.1" },
|
|
|
+ ArithTuple{{1, 1}, {10, -1}, "0.10"},
|
|
|
+ ArithTuple{{"1.1"}, {"1.1"}, "1"},
|
|
|
+// ArithTuple{{"0.01"}, {100, -2}, "1"}, // Infinite loop because of leading 0?
|
|
|
+ ArithTuple{{1, 5}, {1, 5}, "1"},
|
|
|
+// ArithTuple{{1, 5}, {1000000000, -9}, "0.00000000100000"}, // 1,000,000,000.00000000000000 As if multiplied, but with div scale
|
|
|
+// ArithTuple{{100, -2}, {1000000000, -9}, "0.0000001"}, // 100,000,000,000.0000000 As if multiplied, but with div scale
|
|
|
+ ArithTuple{{10000, -4}, {100000, -5}, "0.1"}));
|
|
|
+#pragma clang diagnostic pop
|