summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavis Haupt <davis.haupt@mongodb.com>2022-03-14 19:30:24 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-03-14 21:06:22 +0000
commit314a51dc20c5558d802af836ee838a6479ea843a (patch)
tree58a4bf66bb6bde53871b76c3d974a732013d331b
parent29bc5f6ed98cfa7e7cccd3eecfce2dee2711ea1e (diff)
downloadmongo-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.js13
-rw-r--r--src/mongo/shell/shell_utils.cpp43
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);