diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/platform/decimal128.cpp | 107 | ||||
-rw-r--r-- | src/mongo/platform/decimal128.h | 3 | ||||
-rw-r--r-- | src/mongo/platform/decimal128_test.cpp | 65 |
3 files changed, 101 insertions, 74 deletions
diff --git a/src/mongo/platform/decimal128.cpp b/src/mongo/platform/decimal128.cpp index 1639c7fb749..77b6562a7fd 100644 --- a/src/mongo/platform/decimal128.cpp +++ b/src/mongo/platform/decimal128.cpp @@ -404,25 +404,24 @@ std::string Decimal128::toString() const { */ bid128_to_string(decimalCharRepresentation, dec128, &idec_signaling_flags); - std::string dec128String(decimalCharRepresentation); + StringData dec128String(decimalCharRepresentation); - std::string::size_type ePos = dec128String.find("E"); + int ePos = dec128String.find("E"); // Calculate the precision and exponent of the number and output it in a readable manner int precision = 0; int exponent = 0; - int stringReadPosition = 0; - std::string exponentString = dec128String.substr(ePos); + StringData exponentString = dec128String.substr(ePos); // Get the value of the exponent, start at 2 to ignore the E and the sign - for (std::string::size_type i = 2; i < exponentString.size(); ++i) { + for (size_t i = 2; i < exponentString.size(); ++i) { exponent = exponent * 10 + (exponentString[i] - '0'); } if (exponentString[1] == '-') { exponent *= -1; } - // Get the total precision of the number + // Get the total precision of the number, i.e. the length of the coefficient precision = dec128String.size() - exponentString.size() - 1 /* mantissa sign */; std::string result; @@ -430,55 +429,69 @@ std::string Decimal128::toString() const { // For formatting, leave off the sign if it is positive if (dec128String[0] == '-') result = "-"; - stringReadPosition++; - - int scientificExponent = precision - 1 + exponent; - - // If the number is significantly large, small, or the user has specified an exponent - // such that converting to string would need to append trailing zeros, display the - // number in scientific notation - if (scientificExponent >= 12 || scientificExponent <= -4 || exponent > 0) { - // Output in scientific format - result += dec128String.substr(stringReadPosition, 1); - stringReadPosition++; - precision--; - if (precision) - result += "."; - result += dec128String.substr(stringReadPosition, precision); - // Add the exponent - result += "E"; - if (scientificExponent > 0) - result += "+"; - result += std::to_string(scientificExponent); + + StringData coefficient = dec128String.substr(1, precision); + int adjustedExponent = exponent + precision - 1; + + if (exponent > 0 || adjustedExponent < -6) { + result += _convertToScientificNotation(coefficient, adjustedExponent); } else { - // Regular format with no decimal place - if (exponent >= 0) { - result += dec128String.substr(stringReadPosition, precision); - stringReadPosition += precision; - } else { - int radixPosition = precision + exponent; - if (radixPosition > 0) { - // Non-zero digits before radix point - result += dec128String.substr(stringReadPosition, radixPosition); - stringReadPosition += radixPosition; - } else { - // Leading zero before radix point - result += "0"; - } + result += _convertToStandardDecimalNotation(coefficient, exponent); + } - result += "."; - // Leading zeros after radix point - while (radixPosition++ < 0) - result += "0"; + return result; +} - result += - dec128String.substr(stringReadPosition, precision - std::max(radixPosition - 1, 0)); +std::string Decimal128::_convertToScientificNotation(StringData coefficient, + int adjustedExponent) const { + int cLength = coefficient.size(); + std::string result; + for (int i = 0; i < cLength; i++) { + result += coefficient[i]; + if (i == 0 && cLength > 1) { + result += '.'; } } - + result += 'E'; + if (adjustedExponent > 0) { + result += '+'; + } + result += std::to_string(adjustedExponent); return result; } +std::string Decimal128::_convertToStandardDecimalNotation(StringData coefficient, + int exponent) const { + if (exponent == 0) { + return coefficient.toString(); + } else { + invariant(exponent < 0); + std::string result; + int precision = coefficient.size(); + // Absolute value of the exponent + int significantDecimalDigits = -exponent; + bool decimalAppended = false; + + // Pre-pend 0's before the coefficient as necessary + for (int i = precision; i <= significantDecimalDigits; i++) { + result += '0'; + if (i == precision) { + result += '.'; + decimalAppended = true; + } + } + + // Copy over the digits in the coefficient + for (int i = 0; i < precision; i++) { + if (precision - i == significantDecimalDigits && !decimalAppended) { + result += '.'; + } + result += coefficient[i]; + } + return result; + } +} + bool Decimal128::isZero() const { return bid128_isZero(decimal128ToLibraryType(_value)); } diff --git a/src/mongo/platform/decimal128.h b/src/mongo/platform/decimal128.h index 6fa6d6d3e2c..d736efd9c34 100644 --- a/src/mongo/platform/decimal128.h +++ b/src/mongo/platform/decimal128.h @@ -398,6 +398,9 @@ private: static const uint64_t kCombinationNaN = 0x1f << 12; static const uint64_t kCanonicalCoefficientHighFieldMask = (1ull << 49) - 1; + std::string _convertToScientificNotation(StringData coefficient, int adjustedExponent) const; + std::string _convertToStandardDecimalNotation(StringData coefficient, int exponent) const; + uint64_t _getCombinationField() const { return (_value.high64 >> kCombinationFieldPos) & kCombinationFieldMask; } diff --git a/src/mongo/platform/decimal128_test.cpp b/src/mongo/platform/decimal128_test.cpp index 0632de053e6..a9f09aa893f 100644 --- a/src/mongo/platform/decimal128_test.cpp +++ b/src/mongo/platform/decimal128_test.cpp @@ -119,7 +119,7 @@ TEST(Decimal128Test, TestDoubleConstructorQuant1) { TEST(Decimal128Test, TestDoubleConstructorQuant2) { double dbl = 0.1 / 10000; Decimal128 d(dbl); - ASSERT_EQUALS(d.toString(), "1.00000000000000E-5"); + ASSERT_EQUALS(d.toString(), "0.0000100000000000000"); } TEST(Decimal128Test, TestDoubleConstructorQuant3) { @@ -774,20 +774,6 @@ TEST(Decimal128Test, TestDecimal128ToStringInRangeNeg4Minus) { ASSERT_EQUALS(result, "-0.005"); } -TEST(Decimal128Test, TestDecimal128ToStringOutRangeNeg1) { - std::string s = ".0005"; - Decimal128 d(s); - std::string result = d.toString(); - ASSERT_EQUALS(result, "5E-4"); -} - -TEST(Decimal128Test, TestDecimal128ToStringOutRangeNeg2) { - std::string s = ".000005123123123123"; - Decimal128 d(s); - std::string result = d.toString(); - ASSERT_EQUALS(result, "5.123123123123E-6"); -} - TEST(Decimal128Test, TestDecimal128ToStringOutRangeNeg3) { std::string s = ".012587E-200"; Decimal128 d(s); @@ -795,13 +781,6 @@ TEST(Decimal128Test, TestDecimal128ToStringOutRangeNeg3) { ASSERT_EQUALS(result, "1.2587E-202"); } -TEST(Decimal128Test, TestDecimal128ToStringOutRangePos1) { - std::string s = "1234567890123"; - Decimal128 d(s); - std::string result = d.toString(); - ASSERT_EQUALS(result, "1.234567890123E+12"); -} - TEST(Decimal128Test, TestDecimal128ToStringOutRangePos2) { std::string s = "10201.01E14"; Decimal128 d(s); @@ -809,11 +788,43 @@ TEST(Decimal128Test, TestDecimal128ToStringOutRangePos2) { ASSERT_EQUALS(result, "1.020101E+18"); } -TEST(Decimal128Test, TestDecimal128ToStringOutRangePos3) { - std::string s = "1234567890123456789012345678901234"; - Decimal128 d(s); - std::string result = d.toString(); - ASSERT_EQUALS(result, "1.234567890123456789012345678901234E+33"); +TEST(Decimal128Test, TestDecimal128ToStringFinite) { + // General test cases taken from http://speleotrove.com/decimal/daconvs.html#reftostr + std::string s[15] = {"123", + "-123", + "123E1", + "123E3", + "123E-1", + "123E-5", + "123E-10", + "-123E-12", + "0E0", + "0E-2", + "0E2", + "-0", + "5E-6", + "50E-7", + "5E-7"}; + std::string expected[15] = {"123", + "-123", + "1.23E+3", + "1.23E+5", + "12.3", + "0.00123", + "1.23E-8", + "-1.23E-10", + "0", + "0.00", + "0E+2", + "-0", + "0.000005", + "0.0000050", + "5E-7"}; + for (int i = 0; i < 15; i++) { + Decimal128 d(s[i]); + std::string result = d.toString(); + ASSERT_EQUALS(result, expected[i]); + } } TEST(Decimal128Test, TestDecimal128ToStringInvalidToNaN) { |