// // bigdecimal.t.h // bigdecimal // // Created by Sam Jaffe on 7/5/17. // #pragma once #include "bigdecimal.h" #include class bigdecimal_TestSuite : public CxxTest::TestSuite { public: // void testConstructFromStringIsSameValueAsFromInt() { // using bi = math::bigdecimal; // TS_ASSERT_EQUALS(bi("1000000"), bi(1000000)); // } // void testConstructIntegerAsDecimal() { using bd = math::bigdecimal; TS_ASSERT_EQUALS(bd("1000.00").to_string(), "1000.00"); } void testConstructDecimal() { using bd = math::bigdecimal; TS_ASSERT_EQUALS(bd("1000.10").to_string(), "1000.10"); TS_ASSERT_EQUALS(bd("1000.01").to_string(), "1000.01"); } void testRescaleHigherAddsDigits() { math::bigdecimal dec(100); TS_ASSERT_EQUALS(dec.scale(), 0); dec.rescale(2); TS_ASSERT_EQUALS(dec.to_string(), "100.00"); } void testRescaleLowerCanBeLossy() { math::bigdecimal dec("100.10"); TS_ASSERT_EQUALS(dec.scale(), 2); dec.rescale(0); dec.rescale(2); TS_ASSERT_EQUALS(dec.to_string(), "100.00"); } void testNegativeScaleLosesLowerDigits() { math::bigdecimal dec("123", -2); TS_ASSERT_EQUALS(dec.to_string(), "100"); } void testConstructWithScaleEqualsWithout() { math::bigdecimal scl(100, -2); TS_ASSERT_EQUALS(scl.to_string(), "100"); } void testScaleBelowNegSegDoesntLoseAnything() { TS_ASSERT_EQUALS(math::bigdecimal("1000000000", -9).to_string(), "1000000000"); } void testAddTwoDecimalsSameScale() { using bd = math::bigdecimal; bd a("1000.10"); bd b("1000.01"); TS_ASSERT_EQUALS(a.scale(), b.scale()); TS_ASSERT_EQUALS((a+b).to_string(), "2000.11"); } void testAddTwoDecimalsDifferentScalesUsesHigherScale() { using bd = math::bigdecimal; bd a("1000.10"); bd b("1000"); TS_ASSERT(a.scale() > b.scale()); TS_ASSERT_EQUALS((a+b).to_string(), "2000.10"); } void testAddNumberWithInversePreservesScale() { math::bigdecimal a("1.001"); TS_ASSERT_EQUALS((a+(-a)).to_string(), "0.000"); } void testSubtractTwoDecimalsDifferentScalesUsesHigherScale() { using bd = math::bigdecimal; bd a("900.10"); bd b("1000"); TS_ASSERT(a.scale() > b.scale()); TS_ASSERT_EQUALS((a-b).to_string(), "-99.90"); TS_ASSERT_EQUALS((b-a).to_string(), "99.90"); } void testMultiplicationIncreasesScale() { math::bigdecimal bd("1.1"); auto out = bd * bd; TS_ASSERT_EQUALS(out.scale(), bd.scale() + bd.scale()); TS_ASSERT_EQUALS(out.to_string(), "1.21"); } void testMultiplicationNegativeScaleCountersPositiveScale() { math::bigdecimal a("0.01"); math::bigdecimal b("100", -2); TS_ASSERT_EQUALS((a*b).to_string(), "1"); } void testMultiplicationCreateNewOffsetCell() { math::bigdecimal C(1, 5); TS_ASSERT_EQUALS((C*C).to_string(), "1.0000000000"); } void testMultiplicationNegativeToPositiveScale() { math::bigdecimal C(1, 5); math::bigdecimal D(100, -2); TS_ASSERT_EQUALS((D*C).to_string(), "100.000"); } void testMultiplicationFromDroppedToNonDropScale() { math::bigdecimal C(1, 5); math::bigdecimal E(1000000000, -9); TS_ASSERT_EQUALS((E*C).to_string(), "1000000000"); } void testMultiplicationFromDroppedToLowerScale() { math::bigdecimal D(100, -2); math::bigdecimal E(1000000000, -9); TS_ASSERT_EQUALS((E*D).to_string(), "100000000000"); } void testMultiplicationFromNonDropToDroppedScale() { math::bigdecimal F(10000, -4); math::bigdecimal G(100000, -5); TS_ASSERT_EQUALS((G*F).to_string(), "1000000000"); } math::bigdecimal __create(int32_t scale) { if (scale >= 0) return { "1", scale }; std::vector data(size_t(std::abs(scale))+3, 0); data[0] = '1'; sprintf(data.data()+1, "%0.*d", -scale, 0); return { data.data(), scale }; } std::string __createExpect(int32_t scale1, int32_t scale2) { int32_t decimals{0}, magnitude{std::min(scale1, scale2)}; if (scale1 < 0 == scale2 < 0) { decimals = scale1 + scale2; } else if (scale1 > 0 && scale1 > -scale2) { decimals = scale1 + scale2; } else if (scale2 > 0 && scale2 > -scale1) { decimals = scale1 + scale2; } else { decimals = std::min(scale1, scale2); } if (decimals < 0) { magnitude = decimals; } std::vector data(size_t(std::abs(decimals) + std::abs(magnitude))+4, 0); char * ptr = data.data(); *ptr++ = '1'; if (magnitude < 0) { ptr += sprintf(ptr, "%0.*d", -magnitude, 0); } if (decimals > 0) { sprintf(ptr, ".%0.*d", decimals, 0); } return data.data(); } void __testMultiplyScales(int32_t scale1, int32_t scale2) { math::bigdecimal A(__create(scale1)); math::bigdecimal B(__create(scale2)); // This is wrong std::string expected(__createExpect(scale1, scale2)); TS_ASSERT_EQUALS((A*B).to_string(), expected); TS_ASSERT_EQUALS((B*A).to_string(), expected); } void testMultiplicationBrutePermutations() { for (int32_t i = -20; i <= +20; ++i) { for (int32_t j = i; j <= +20; ++j) { __testMultiplyScales(i, j); } } } void testDivideHigherScaleByLowerScale() { math::bigdecimal a("1", 2); math::bigdecimal b("1", 1); TS_ASSERT_EQUALS((a/b).to_string(), "1.0"); } void testDivideLowerScaleByHigherScale() { math::bigdecimal a("10", 1); math::bigdecimal b("1", 2); TS_ASSERT_EQUALS((a/b).to_string(), "10"); } };