summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVincent Do <do.vincent@live.com>2016-05-24 14:20:10 -0400
committerVincent Do <vincent.do@mongodb.com>2016-05-31 13:24:20 -0400
commit5b6b62d54476a5ce8631847ef0f4a2315f341cc6 (patch)
tree28041e0f1ad843cfc2194ed0277ead0a8e7fdc22 /src
parent5df895a08fd368d124ba69239e6d311216ee4289 (diff)
downloadmongo-5b6b62d54476a5ce8631847ef0f4a2315f341cc6.tar.gz
SERVER-23263 - Make Decimal128 toString() conform to new spec
Diffstat (limited to 'src')
-rw-r--r--src/mongo/platform/decimal128.cpp107
-rw-r--r--src/mongo/platform/decimal128.h3
-rw-r--r--src/mongo/platform/decimal128_test.cpp65
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) {