diff options
author | Davis Haupt <davis.haupt@mongodb.com> | 2022-03-14 19:30:24 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-03-14 21:06:22 +0000 |
commit | 314a51dc20c5558d802af836ee838a6479ea843a (patch) | |
tree | 58a4bf66bb6bde53871b76c3d974a732013d331b | |
parent | 29bc5f6ed98cfa7e7cccd3eecfce2dee2711ea1e (diff) | |
download | mongo-314a51dc20c5558d802af836ee838a6479ea843a.tar.gz |
SERVER-64226 Add shell helper to check equality up to a specified number of digits for NumberDecimal
-rw-r--r-- | jstests/decimal/decimal_constructors.js | 13 | ||||
-rw-r--r-- | src/mongo/shell/shell_utils.cpp | 43 |
2 files changed, 56 insertions, 0 deletions
diff --git a/jstests/decimal/decimal_constructors.js b/jstests/decimal/decimal_constructors.js index 8af1b6d0f62..630a9b4d3a5 100644 --- a/jstests/decimal/decimal_constructors.js +++ b/jstests/decimal/decimal_constructors.js @@ -47,4 +47,17 @@ assert(numberDecimalsEqual(NumberDecimal('10.20'), NumberDecimal('10.2'))); assert.throws( () => numberDecimalsEqual(NumberDecimal('10.20'), NumberDecimal('10.2'), "Third parameter")); assert.throws(() => numberDecimalsEqual(NumberDecimal('10.20'), "Wrong parameter type")); + +// Verify the behavior of 'numberDecimalsAlmostEqual' helper. +assert(numberDecimalsAlmostEqual(NumberDecimal("10001"), NumberDecimal("10002"), 3)); +assert.neq(numberDecimalsAlmostEqual(NumberDecimal("10001"), NumberDecimal("10002"), 5)); + +// Regression tests for BF-24149. +assert(numberDecimalsAlmostEqual(NumberDecimal("905721242210.0455427920454969568"), + NumberDecimal("905721242210.0453137831269007622941"), + 15)); + +assert.neq(numberDecimalsAlmostEqual(NumberDecimal("905721242210.0455427920454969568"), + NumberDecimal("905721242210.0453137831269007622941"), + 16)); }()); diff --git a/src/mongo/shell/shell_utils.cpp b/src/mongo/shell/shell_utils.cpp index 0ad7323097b..2ce36f9ada4 100644 --- a/src/mongo/shell/shell_utils.cpp +++ b/src/mongo/shell/shell_utils.cpp @@ -52,6 +52,7 @@ #include "mongo/db/auth/security_token.h" #include "mongo/db/hasher.h" #include "mongo/logv2/log.h" +#include "mongo/platform/decimal128.h" #include "mongo/platform/mutex.h" #include "mongo/platform/random.h" #include "mongo/scripting/engine.h" @@ -497,6 +498,47 @@ BSONObj numberDecimalsEqual(const BSONObj& input, void*) { return BSON("" << first.numberDecimal().isEqual(second.numberDecimal())); } +BSONObj numberDecimalsAlmostEqual(const BSONObj& input, void*) { + if (input.nFields() != 3) { + return BSON("" << false); + } + + BSONObjIterator i(input); + auto first = i.next(); + auto second = i.next(); + auto third = i.next(); + + // Type-check arguments before performing any calculations. + if (!(first.type() == BSONType::NumberDecimal && second.type() == BSONType::NumberDecimal && + third.isNumber())) { + return BSON("" << false); + } + + auto a = first.numberDecimal(); + auto b = second.numberDecimal(); + + // 10.0 is used frequently in the rest of the function, so save it to a variable. + auto ten = Decimal128(10); + auto exponent = a.logarithm(ten).toAbs().round(); + + // Return early if arguments are not the same order of magnitude. + if (exponent != b.logarithm(ten).toAbs().round()) { + return BSON("" << false); + } + + // Put the whole number behind the decimal point. + a = a.divide(ten.power(exponent)); + b = b.divide(ten.power(exponent)); + + auto places = third.numberDecimal(); + auto isErrorAcceptable = a.subtract(b) + .toAbs() + .multiply(ten.power(places, Decimal128::kRoundTowardZero)) + .round(Decimal128::kRoundTowardZero) == Decimal128(0); + + return BSON("" << isErrorAcceptable); +} + void installShellUtils(Scope& scope) { scope.injectNative("getMemInfo", JSGetMemInfo); scope.injectNative("_createSecurityToken", _createSecurityToken); @@ -512,6 +554,7 @@ void installShellUtils(Scope& scope) { scope.injectNative("fileExists", fileExistsJS); scope.injectNative("isInteractive", isInteractive); scope.injectNative("numberDecimalsEqual", numberDecimalsEqual); + scope.injectNative("numberDecimalsAlmostEqual", numberDecimalsAlmostEqual); installShellUtilsLauncher(scope); installShellUtilsExtended(scope); |