1 2 /** 3 * Copyright (C) 2018-present MongoDB, Inc. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the Server Side Public License, version 1, 7 * as published by MongoDB, Inc. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * Server Side Public License for more details. 13 * 14 * You should have received a copy of the Server Side Public License 15 * along with this program. If not, see 16 * <http://www.mongodb.com/licensing/server-side-public-license>. 17 * 18 * As a special exception, the copyright holders give permission to link the 19 * code of portions of this program with the OpenSSL library under certain 20 * conditions as described in each individual source file and distribute 21 * linked combinations including the program with the OpenSSL library. You 22 * must comply with the Server Side Public License in all respects for 23 * all of the code used other than as permitted herein. If you modify file(s) 24 * with this exception, you may extend this exception to your version of the 25 * file(s), but you are not obligated to do so. If you do not wish to do so, 26 * delete this exception statement from your version. If you delete this 27 * exception statement from all source files in the program, then also delete 28 * it in the license file. 29 */ 30 31 #pragma once 32 33 #include <array> 34 #include <cstdint> 35 #include <iostream> 36 #include <string> 37 #include <utility> 38 39 #include "mongo/config.h" 40 41 #include "mongo/util/assert_util.h" 42 43 namespace mongo { 44 45 /** 46 * Wrapper class for the MongoDB Decimal128 data type. Sample usage: 47 * Decimal128 d1("+10.0"); 48 * Decimal128 d2("+0.1") 49 * Decimal128 sum = d1.add(d2) 50 * std::cout << sum << std::endl; 51 */ 52 class Decimal128 { 53 public: 54 /** 55 * Static constants to get Decimal128 representations of specific numbers 56 * kLargestPositive -> 9999999999999999999999999999999999E6111 57 * kSmallestPositive -> 1E-6176 58 * kLargestNegative -> -9999999999999999999999999999999999E6111 59 * kSmallestNegative -> -1E-6176 60 * kLargestNegativeExponentZero -> 0E-6176 61 */ 62 static const Decimal128 kLargestPositive; 63 static const Decimal128 kSmallestPositive; 64 static const Decimal128 kLargestNegative; 65 static const Decimal128 kSmallestNegative; 66 67 static const Decimal128 kNormalizedZero; // zero with exponent 0 68 static const Decimal128 kLargestNegativeExponentZero; 69 70 static const Decimal128 kPositiveInfinity; 71 static const Decimal128 kNegativeInfinity; 72 static const Decimal128 kPositiveNaN; 73 static const Decimal128 kNegativeNaN; 74 75 static const uint32_t kMaxBiasedExponent = 6143 + 6144; 76 // Biased exponent of a Decimal128 with least significant digit in the units place 77 static const int32_t kExponentBias = 6143 + 33; 78 static const uint32_t kInfinityExponent = kMaxBiasedExponent + 1; // internal convention only 79 80 /** 81 * This struct holds the raw data for IEEE 754-2008 data types 82 */ 83 struct Value { 84 std::uint64_t low64; 85 std::uint64_t high64; 86 }; 87 88 enum RoundingMode { 89 kRoundTiesToEven = 0, 90 kRoundTowardNegative = 1, 91 kRoundTowardPositive = 2, 92 kRoundTowardZero = 3, 93 kRoundTiesToAway = 4 94 }; 95 96 /** 97 * Indicates if constructing a Decimal128 from a double should round the double to 15 digits 98 * (so the conversion will correctly round-trip decimals), or round to the full 34 digits. 99 */ 100 enum RoundingPrecision { kRoundTo15Digits = 0, kRoundTo34Digits = 1 }; 101 102 /** 103 * The signaling flag enum determines the signaling nature of a decimal operation. 104 * The values of these flags are defined in the Intel RDFP math library. 105 * 106 * The provided hasFlag method checks whether provided signalingFlags contains flag f. 107 * 108 * Example: 109 * Decimal128 dcml = Decimal128('0.1'); 110 * std::uint32_t sigFlag = Decimal128::SignalingFlag::kNoFlag; 111 * double dbl = dcml.toDouble(&sigFlag); 112 * if Decimal128::hasFlag(sigFlag, SignalingFlag::kInexact) 113 * cout << "inexact decimal to double conversion!" << endl; 114 */ 115 enum SignalingFlag { 116 kNoFlag = 0x00, 117 kInvalid = 0x01, 118 kDivideByZero = 0x04, 119 kOverflow = 0x08, 120 kUnderflow = 0x10, 121 kInexact = 0x20, 122 }; 123 hasFlag(std::uint32_t signalingFlags,SignalingFlag f)124 static bool hasFlag(std::uint32_t signalingFlags, SignalingFlag f) { 125 return ((signalingFlags & f) != 0u); 126 } 127 128 /** 129 * Returns true if a valid Decimal can be constructed from the given arguments. 130 */ isValid(uint64_t sign,uint64_t exponent,uint64_t coefficientHigh,uint64_t coefficientLow)131 static bool isValid(uint64_t sign, 132 uint64_t exponent, 133 uint64_t coefficientHigh, 134 uint64_t coefficientLow) { 135 if (coefficientHigh >= 0x1ed09bead87c0 && 136 (coefficientHigh != 0x1ed09bead87c0 || coefficientLow != 0x378d8e63ffffffff)) { 137 return false; 138 } 139 auto value = 140 Value{coefficientLow, 141 (sign << kSignFieldPos) | (exponent << kExponentFieldPos) | coefficientHigh}; 142 143 return Decimal128(value).getBiasedExponent() == exponent; 144 } 145 146 /** 147 * Construct a 0E0 valued Decimal128. 148 */ Decimal128()149 Decimal128() : _value(kNormalizedZero._value) {} 150 151 /** 152 * This constructor takes in a raw decimal128 type, which consists of two 153 * uint64_t's. This class performs an endian check on the system to ensure 154 * that the Value.high64 represents the higher 64 bits. 155 */ Decimal128(Decimal128::Value dec128Value)156 explicit Decimal128(Decimal128::Value dec128Value) : _value(dec128Value) {} 157 158 /** 159 * Constructs a Decimal128 from parts, dealing with proper encoding of the combination field. 160 * Assumes that the value will be inside the valid range of finite values. (No NaN/Inf, etc.) 161 */ Decimal128(uint64_t sign,uint64_t exponent,uint64_t coefficientHigh,uint64_t coefficientLow)162 Decimal128(uint64_t sign, uint64_t exponent, uint64_t coefficientHigh, uint64_t coefficientLow) 163 : _value( 164 Value{coefficientLow, 165 (sign << kSignFieldPos) | (exponent << kExponentFieldPos) | coefficientHigh}) { 166 dassert(isValid(sign, exponent, coefficientHigh, coefficientLow)); 167 } 168 169 explicit Decimal128(std::int32_t int32Value); 170 explicit Decimal128(std::int64_t int64Value); 171 172 /** 173 * This constructor takes a double and constructs a Decimal128 object given a roundMode, either 174 * to full precision, or with a fixed precision of 15 decimal digits. When a double is used to 175 * store a decimal floating point number, it is only correct up to 15 digits after converting 176 * back to decimal, so the 15 digit rounding is used for mixed-mode operations. 177 * The general idea is to quantize the direct double->dec128 conversion 178 * with a quantum of 1E(-15 +/- base10 exponent equivalent of the double). 179 * To do this, we find the smallest (abs value) base 10 exponent greater 180 * than the double's base 2 exp and shift the quantizer's exp accordingly. 181 */ 182 explicit Decimal128(double doubleValue, 183 RoundingPrecision roundPrecision = kRoundTo15Digits, 184 RoundingMode roundMode = kRoundTiesToEven); 185 186 /** 187 * This constructor takes a string and constructs a Decimal128 object from it. 188 * Inputs larger than 34 digits of precision are rounded according to the 189 * specified rounding mode. The following (and variations) are all accepted: 190 * "+2.02E200" 191 * "2.02E+200" 192 * "-202E-500" 193 * "somethingE200" --> NaN 194 * "200E9999999999" --> +Inf 195 * "-200E9999999999" --> -Inf 196 */ 197 explicit Decimal128(std::string stringValue, RoundingMode roundMode = kRoundTiesToEven); 198 199 Decimal128(std::string stringValue, 200 std::uint32_t* signalingFlag, 201 RoundingMode roundMode = kRoundTiesToEven); 202 203 /** 204 * This function gets the inner Value struct storing a Decimal128 value. 205 */ 206 Value getValue() const; 207 208 /** 209 * Extracts the biased exponent from the combination field. 210 */ getBiasedExponent()211 uint32_t getBiasedExponent() const { 212 const uint64_t combo = _getCombinationField(); 213 if (combo < kCombinationNonCanonical) 214 return combo >> 3; 215 216 return combo >= kCombinationInfinity 217 ? kMaxBiasedExponent + 1 // NaN or Inf 218 : (combo >> 1) & ((1 << 14) - 1); // non-canonical representation 219 } 220 221 /** 222 * Returns the high 49 bits of the 113-bit binary encoded coefficient. Returns 0 for 223 * non-canonical or non-finite numbers. 224 */ getCoefficientHigh()225 uint64_t getCoefficientHigh() const { 226 return _getCombinationField() < kCombinationNonCanonical 227 ? _value.high64 & kCanonicalCoefficientHighFieldMask 228 : 0; 229 } 230 231 /** 232 * Returns the low 64 bits of the 113-bit binary encoded coefficient. Returns 0 for 233 * non-canonical or non-finite numbers. 234 */ getCoefficientLow()235 uint64_t getCoefficientLow() const { 236 return _getCombinationField() < kCombinationNonCanonical ? _value.low64 : 0; 237 } 238 239 /** 240 * Returns the absolute value of this. 241 */ 242 Decimal128 toAbs() const; 243 244 /** 245 * Returns `this` with inverted sign bit 246 */ negate()247 Decimal128 negate() const { 248 Value negated = {_value.low64, _value.high64 ^ (1ULL << 63)}; 249 return Decimal128(negated); 250 } 251 252 253 /** 254 * This set of functions converts a Decimal128 to a certain integer type with a 255 * given rounding mode. 256 * 257 * Each function is overloaded to provide an optional signalingFlags output parameter 258 * that can be set to one of the Decimal128::SignalingFlag enumerators: 259 * kNoFlag, kInvalid 260 * 261 * Note: The signaling flags for these functions only signal 262 * an invalid conversion. If inexact conversion flags are necessary, call 263 * the toTypeExact version of the function defined below. This set of operations 264 * (toInt, toLong) has better performance than the latter. 265 */ 266 std::int32_t toInt(RoundingMode roundMode = kRoundTiesToEven) const; 267 std::int32_t toInt(std::uint32_t* signalingFlags, 268 RoundingMode roundMode = kRoundTiesToEven) const; 269 std::int64_t toLong(RoundingMode roundMode = kRoundTiesToEven) const; 270 std::int64_t toLong(std::uint32_t* signalingFlags, 271 RoundingMode roundMode = kRoundTiesToEven) const; 272 273 /** 274 * This set of functions converts a Decimal128 to a certain integer type with a 275 * given rounding mode. The signaling flags for these functions will also signal 276 * inexact computation. 277 * 278 * Each function is overloaded to provide an optional signalingFlags output parameter 279 * that can be set to one of the Decimal128::SignalingFlag enumerators: 280 * kNoFlag, kInexact, kInvalid 281 */ 282 std::int32_t toIntExact(RoundingMode roundMode = kRoundTiesToEven) const; 283 std::int32_t toIntExact(std::uint32_t* signalingFlags, 284 RoundingMode roundMode = kRoundTiesToEven) const; 285 std::int64_t toLongExact(RoundingMode roundMode = kRoundTiesToEven) const; 286 std::int64_t toLongExact(std::uint32_t* signalingFlags, 287 RoundingMode roundMode = kRoundTiesToEven) const; 288 289 /** 290 * These functions convert decimals to doubles and have the ability to signal 291 * inexact, underflow, overflow, and invalid operation. 292 * 293 * This function is overloaded to provide an optional signalingFlags output parameter 294 * that can be set to one of the Decimal128::SignalingFlag enumerators: 295 * kNoFlag, kInexact, kUnderflow, kOverflow, kInvalid 296 */ 297 double toDouble(RoundingMode roundMode = kRoundTiesToEven) const; 298 double toDouble(std::uint32_t* signalingFlags, RoundingMode roundMode = kRoundTiesToEven) const; 299 300 /** 301 * This function converts a Decimal128 to a string with the following semantics: 302 * 303 * Suppose Decimal128 D has P significant digits and exponent Exp. 304 * Define SE to be the scientific exponent of D equal to Exp + P - 1. 305 * 306 * Define format E as normalized scientific notation (ex: 1.0522E+16) 307 * Define format F as a regular formatted number with no exponent (ex: 105.22) 308 * 309 * In order to improve decimal type readability, 310 * if SE >= 12 or SE <= -4, use format E to display D. 311 * if Exp > 0, use format E to display D because adding trailing zeros implies 312 * extra, incorrect precision 313 * 314 * Otherwise, display using F with no exponent (add leading zeros if necessary). 315 * 316 * This conversion to string is roughly based on the G C99 printf specifier and 317 * existing behavior for the double numeric type in MongoDB. 318 */ 319 std::string toString() const; 320 321 /** 322 * This set of functions check whether a Decimal128 is Zero, NaN, or +/- Inf 323 */ 324 bool isZero() const; 325 bool isNaN() const; 326 bool isInfinite() const; 327 bool isNegative() const; 328 329 /** 330 * Return true if and only if a Decimal128 is Zero, Normal, or Subnormal (not Inf or NaN) 331 */ 332 bool isFinite() const; 333 334 /** 335 * This set of mathematical operation functions implement the corresponding 336 * IEEE 754-2008 operations on self and other. 337 * The 'add' and 'multiply' methods are commutative, so a.add(b) is equivalent to b.add(a). 338 * Rounding of results that require a precision greater than 34 decimal digits 339 * is performed using the supplied rounding mode (defaulting to kRoundTiesToEven). 340 * NaNs and infinities are handled according to the IEEE 754-2008 specification. 341 * 342 * Each function is overloaded to provide an optional signalingFlags output parameter 343 * that can be set to one of the Decimal128::SignalingFlag enumerators: 344 * kNoFlag, kInexact, kUnderflow, kOverflow, kInvalid 345 * 346 * The divide operation may also set signalingFlags to kDivideByZero 347 */ 348 Decimal128 add(const Decimal128& other, RoundingMode roundMode = kRoundTiesToEven) const; 349 Decimal128 add(const Decimal128& other, 350 std::uint32_t* signalingFlags, 351 RoundingMode roundMode = kRoundTiesToEven) const; 352 Decimal128 subtract(const Decimal128& other, RoundingMode roundMode = kRoundTiesToEven) const; 353 Decimal128 subtract(const Decimal128& other, 354 std::uint32_t* signalingFlags, 355 RoundingMode roundMode = kRoundTiesToEven) const; 356 Decimal128 multiply(const Decimal128& other, RoundingMode roundMode = kRoundTiesToEven) const; 357 Decimal128 multiply(const Decimal128& other, 358 std::uint32_t* signalingFlags, 359 RoundingMode roundMode = kRoundTiesToEven) const; 360 Decimal128 divide(const Decimal128& other, RoundingMode roundMode = kRoundTiesToEven) const; 361 Decimal128 divide(const Decimal128& other, 362 std::uint32_t* signalingFlags, 363 RoundingMode roundMode = kRoundTiesToEven) const; 364 Decimal128 exponential(RoundingMode roundMode = kRoundTiesToEven) const; 365 Decimal128 exponential(std::uint32_t* signalingFlags, 366 RoundingMode roundMode = kRoundTiesToEven) const; 367 Decimal128 logarithm(RoundingMode roundMode = kRoundTiesToEven) const; 368 Decimal128 logarithm(std::uint32_t* signalingFlags, 369 RoundingMode roundMode = kRoundTiesToEven) const; 370 Decimal128 logarithm(const Decimal128& other, RoundingMode roundMode = kRoundTiesToEven) const; 371 Decimal128 logarithm(const Decimal128& other, 372 std::uint32_t* signalingFlags, 373 RoundingMode roundMode = kRoundTiesToEven) const; 374 Decimal128 modulo(const Decimal128& other) const; 375 Decimal128 modulo(const Decimal128& other, std::uint32_t* signalingFlags) const; 376 377 Decimal128 power(const Decimal128& other, RoundingMode roundMode = kRoundTiesToEven) const; 378 Decimal128 power(const Decimal128& other, 379 std::uint32_t* signalingFlags, 380 RoundingMode roundMode = kRoundTiesToEven) const; 381 382 Decimal128 squareRoot(RoundingMode roundMode = kRoundTiesToEven) const; 383 Decimal128 squareRoot(std::uint32_t* signalingFlags, 384 RoundingMode roundMode = kRoundTiesToEven) const; 385 386 /** 387 * This function quantizes the current decimal given a quantum reference 388 */ 389 Decimal128 quantize(const Decimal128& reference, 390 RoundingMode roundMode = kRoundTiesToEven) const; 391 Decimal128 quantize(const Decimal128& reference, 392 std::uint32_t* signalingFlags, 393 RoundingMode roundMode = kRoundTiesToEven) const; 394 395 /** 396 * This function normalizes the cohort of a Decimal128 by forcing it to maximum 397 * precision (34 decimal digits). This normalization is important when it is desirable 398 * to force equal decimals of different representations (i.e. 5.0 and 5.00) to equal 399 * decimals with the same representation (5000000000000000000000000000000000E-33). 400 * Hashing equal decimals to equal hashes becomes possible with such normalization. 401 */ normalize()402 Decimal128 normalize() const { 403 // Normalize by adding 0E-6176 which forces a decimal to maximum precision (34 digits) 404 return add(kLargestNegativeExponentZero); 405 } 406 407 /** 408 * This set of comparison operations takes a single Decimal128 and returns a boolean 409 * noting the value of the comparison. These comparisons are not total ordered, but 410 * comply with the IEEE 754-2008 spec. The comparison returns true if the caller 411 * is <equal, notequal, greater, greaterequal, less, lessequal> the argument (other). 412 */ 413 bool isEqual(const Decimal128& other) const; 414 bool isNotEqual(const Decimal128& other) const; 415 bool isGreater(const Decimal128& other) const; 416 bool isGreaterEqual(const Decimal128& other) const; 417 bool isLess(const Decimal128& other) const; 418 bool isLessEqual(const Decimal128& other) const; 419 420 /** 421 * Returns true iff 'this' and 'other' are bitwise identical. Note that this returns false 422 * even for values that may convert to identical strings, such as different NaNs or 423 * non-canonical representations that represent bit-patterns never generated by any conforming 424 * implementation, but should be treated as 0. Mostly for testing. 425 */ isBinaryEqual(const Decimal128 & other)426 bool isBinaryEqual(const Decimal128& other) const { 427 return _value.high64 == other._value.high64 && _value.low64 == other._value.low64; 428 } 429 430 private: 431 static const uint8_t kSignFieldPos = 64 - 1; 432 static const uint8_t kCombinationFieldPos = kSignFieldPos - 17; 433 static const uint64_t kCombinationFieldMask = (1 << 17) - 1; 434 static const uint64_t kExponentFieldPos = kCombinationFieldPos + 3; 435 static const uint64_t kCoefficientContinuationFieldMask = (1ull << kCombinationFieldPos) - 1; 436 static const uint64_t kCombinationNonCanonical = 3 << 15; 437 static const uint64_t kCombinationInfinity = 0x1e << 12; 438 static const uint64_t kCombinationNaN = 0x1f << 12; 439 static const uint64_t kCanonicalCoefficientHighFieldMask = (1ull << 49) - 1; 440 441 std::string _convertToScientificNotation(StringData coefficient, int adjustedExponent) const; 442 std::string _convertToStandardDecimalNotation(StringData coefficient, int exponent) const; 443 _getCombinationField()444 uint64_t _getCombinationField() const { 445 return (_value.high64 >> kCombinationFieldPos) & kCombinationFieldMask; 446 } 447 448 Value _value; 449 }; 450 } // namespace mongo 451