Browse Source

More test magic. Fixing bug in rescaling

Sam Jaffe 7 years ago
parent
commit
097d356053
3 changed files with 81 additions and 100 deletions
  1. 1 1
      src/bigdecimal.cpp
  2. 75 93
      test/bigdecimal_test.cpp
  3. 5 6
      test/biginteger_test.cpp

+ 1 - 1
src/bigdecimal.cpp

@@ -201,7 +201,7 @@ bigdecimal & math::operator/=(bigdecimal & rhs, bigdecimal const & lhs) {
     if (rhs.scale() < new_scale) rhs.rescale(new_scale);
     auto cmp = detail::compare(rhs.data, lhs.data);
     if (cmp < 0) { rhs = bigdecimal::ZERO; }
-    else if (cmp == 0) { rhs.data = {1}; }
+    else if (cmp == 0) { return rhs = {1, new_scale}; }
     else {
       rhs.data = detail::divide(rhs.data, lhs.data);
       auto diff = add_scale(new_scale) - rhs.steps_ - lhs.steps_;

+ 75 - 93
test/bigdecimal_test.cpp

@@ -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

+ 5 - 6
test/biginteger_test.cpp

@@ -52,21 +52,20 @@ TEST_P(BigIntModuloTest, IsZero) {
   EXPECT_THAT(std::get<0>(pair)%std::get<1>(pair), ZERO);
 }
 
-class BigIntegerLtTest : public testing::TestWithParam<BigIntPair> {};
+class LtTest : public testing::TestWithParam<BigIntPair> {};
 
-TEST_P(BigIntegerLtTest, IsLessThan) {
+TEST_P(LtTest, IsLessThan) {
   auto pair = GetParam();
   EXPECT_THAT(std::get<0>(pair), testing::Lt(std::get<1>(pair)));
 }
 
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
-INSTANTIATE_TEST_CASE_P(SmallNum, BigIntegerLtTest,
+INSTANTIATE_TEST_CASE_P(BigInteger, LtTest,
                         testing::Values(BigIntPair{-1,  1},
                                         BigIntPair{ 0,  1},
-                                        BigIntPair{-2, -1}));
-INSTANTIATE_TEST_CASE_P(LargeNum, BigIntegerLtTest,
-                        testing::Values(BigIntPair{ 1000000000,  1000000001},
+                                        BigIntPair{-2, -1},
+                                        BigIntPair{ 1000000000,  1000000001},
                                         BigIntPair{-1000000001, -1000000000}));
 
 INSTANTIATE_TEST_CASE_P(ZeroModAny, BigIntModuloTest,