summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorKevinCybura <KevinCybura@gmail.com>2018-02-06 15:14:26 -0500
committerCharlie Swanson <charlie.swanson@mongodb.com>2018-02-10 11:52:43 -0500
commite6e8dcf826c8e25b53c8368a0d538dfbdeaee589 (patch)
treeb0461f127e4723b86ab7cd29cac637b9c1448914 /src/mongo/db
parentc4c4bef71ecd64db91e1252200d82a2f5c265cc6 (diff)
downloadmongo-e6e8dcf826c8e25b53c8368a0d538dfbdeaee589.tar.gz
SERVER-32873 Fix incorrect handling of NumberLong in $pow function
Signed-off-by: Charlie Swanson <charlie.swanson@mongodb.com> Closes #1211
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/pipeline/expression.cpp15
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp16
2 files changed, 28 insertions, 3 deletions
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 3c4b5ef3dae..3ab0c300cd3 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -3154,14 +3154,23 @@ Value ExpressionPow::evaluate(const Document& root) const {
long long baseLong = baseVal.getLong();
long long expLong = expVal.getLong();
- // If the result cannot be represented as a long, return a double. Otherwise if either number
- // is a long, return a long. If both numbers are ints, then return an int if the result fits or
- // a long if it is too big.
+ // If the result cannot be represented as a long, return a double. Otherwise if either number is
+ // a long, return a long. If both numbers are ints, then return an int if the result fits or a
+ // long if it is too big.
if (!representableAsLong(baseLong, expLong)) {
return Value(std::pow(baseLong, expLong));
}
long long result = 1;
+
+ // When 'baseLong' == -1 and 'expLong' is < 0 the following for loop will never run because
+ // 'expLong' will always be less than 0 so result will always be 1. This is not always correct
+ // because the result can potentially be -1. ex: 'baselong' = -1 'expLong' = -5 then result
+ // should be -1.
+ if (baseLong == -1 && expLong < 0) {
+ expLong = expLong % 2 == 0 ? 2 : 1;
+ }
+
// Use repeated multiplication, since pow() casts args to doubles which could result in loss of
// precision if arguments are very large.
for (int i = 0; i < expLong; i++) {
diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp
index b2610e97bdb..345c5042f09 100644
--- a/src/mongo/db/pipeline/expression_test.cpp
+++ b/src/mongo/db/pipeline/expression_test.cpp
@@ -2193,6 +2193,22 @@ TEST(ExpressionFromAccumulators, StdDevSamp) {
{{}, Value(BSONNULL)}});
}
+TEST(ExpressionPowTest, NegativeOneRaisedToNegativeOddExponentShouldOutPutNegativeOne) {
+ assertExpectedResults("$pow",
+ {
+ {{Value(-1), Value(-1)}, Value(-1)},
+ {{Value(-1), Value(-2)}, Value(1)},
+ {{Value(-1), Value(-3)}, Value(-1)},
+
+ {{Value(-1LL), Value(0LL)}, Value(1LL)},
+ {{Value(-1LL), Value(-1LL)}, Value(-1LL)},
+ {{Value(-1LL), Value(-2LL)}, Value(1LL)},
+ {{Value(-1LL), Value(-3LL)}, Value(-1LL)},
+ {{Value(-1LL), Value(-4LL)}, Value(1LL)},
+ {{Value(-1LL), Value(-5LL)}, Value(-1LL)},
+ });
+}
+
namespace FieldPath {
/** The provided field path does not pass validation. */