diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultOperations.java b/src/main/java/ch/njol/skript/classes/data/DefaultOperations.java index 1588d31ece8..48bf82939fd 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultOperations.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultOperations.java @@ -32,26 +32,53 @@ public class DefaultOperations { static { // Number - Number Arithmetics.registerOperation(Operator.ADDITION, Number.class, (left, right) -> { - if (Utils.isInteger(left, right)) - return left.longValue() + right.longValue(); + if (Utils.isInteger(left, right)) { + long result = left.longValue() + right.longValue(); + // catches overflow, from Math.addExact(long, long) + if (((left.longValue() ^ result) & (right.longValue() ^ result)) >= 0) + return result; + } return left.doubleValue() + right.doubleValue(); }); Arithmetics.registerOperation(Operator.SUBTRACTION, Number.class, (left, right) -> { - if (Utils.isInteger(left, right)) - return left.longValue() - right.longValue(); + if (Utils.isInteger(left, right)) { + long result = left.longValue() - right.longValue(); + // catches overflow, from Math.addExact(long, long) + if (((left.longValue() ^ result) & (right.longValue() ^ result)) >= 0) + return result; + } return left.doubleValue() - right.doubleValue(); }); Arithmetics.registerOperation(Operator.MULTIPLICATION, Number.class, (left, right) -> { - if (Utils.isInteger(left, right)) - return left.longValue() * right.longValue(); - return left.doubleValue() * right.doubleValue(); + if (!Utils.isInteger(left, right)) + return left.doubleValue() * right.doubleValue(); + + // catch overflow, from Math.multiplyExact(long, long) + long longLeft = left.longValue(); + long longRight = right.longValue(); + long ax = Math.abs(longLeft); + long ay = Math.abs(longRight); + + long result = left.longValue() * right.longValue(); + + if (((ax | ay) >>> 31 != 0)) { + // Some bits greater than 2^31 that might cause overflow + // Check the result using the divide operator + // and check for the special case of Long.MIN_VALUE * -1 + if (((longRight != 0) && (result / longRight != longLeft)) || + (longLeft == Long.MIN_VALUE && longRight == -1)) { + return left.doubleValue() * right.doubleValue(); + } + } + return result; }); Arithmetics.registerOperation(Operator.DIVISION, Number.class, (left, right) -> left.doubleValue() / right.doubleValue()); Arithmetics.registerOperation(Operator.EXPONENTIATION, Number.class, (left, right) -> Math.pow(left.doubleValue(), right.doubleValue())); Arithmetics.registerDifference(Number.class, (left, right) -> { - if (Utils.isInteger(left, right)) - return Math.abs(left.longValue() - right.longValue()); - return Math.abs(left.doubleValue() - right.doubleValue()); + double result = Math.abs(left.doubleValue() - right.doubleValue()); + if (Utils.isInteger(left, right) && result < Long.MAX_VALUE && result > Long.MIN_VALUE) + return (long) result; + return result; }); Arithmetics.registerDefaultValue(Number.class, () -> 0L); diff --git a/src/test/skript/tests/regressions/7209-long overflow.sk b/src/test/skript/tests/regressions/7209-long overflow.sk new file mode 100644 index 00000000000..bff56f0f57f --- /dev/null +++ b/src/test/skript/tests/regressions/7209-long overflow.sk @@ -0,0 +1,34 @@ +test "long overflow, addition": + set {_double-const} to 9000000000000000000.0 + set {_long-const} to 9000000000000000000 + + loop 100 times: + set {_double} to {_double} + {_double-const} + set {_long} to {_long} + {_long-const} + assert {_double} is {_long} with "long value did not overflow to a double" + + assert {_long} is 9000000000000000000 * 100.0 with "wrong final value" + +test "long underflow, subtraction": + set {_double-const} to 9000000000000000000.0 + set {_long-const} to 9000000000000000000 + + loop 100 times: + set {_double} to {_double} - {_double-const} + set {_long} to {_long} - {_long-const} + assert {_double} is {_long} with "long value did not overflow to a double" + + assert {_long} is 9000000000000000000 * -100.0 with "wrong final value" + +test "long overflow, multiplication": + set {_double} to 10.0 + set {_long} to 10 + + loop 100 times: + set {_double} to {_double} * 10 + set {_long} to {_long} * 10 + assert {_double} is {_long} with "long value did not overflow to a double" + + # the 6 is due to floating point error + assert {_long} is 100000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000 with "wrong final value" +