Skip to content

Commit 558d39f

Browse files
committed
[GR-23245] Improved precision of int.__truediv__
PullRequest: graalpython/1039
2 parents d22111b + 3cc70a3 commit 558d39f

File tree

4 files changed

+29
-11
lines changed

4 files changed

+29
-11
lines changed

graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/grammar/BinaryArithTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2018, Oracle and/or its affiliates.
2+
* Copyright (c) 2017, 2020, Oracle and/or its affiliates.
33
* Copyright (c) 2013, Regents of the University of California
44
*
55
* All rights reserved.
@@ -110,7 +110,7 @@ public void complexBinaryArith2() {
110110
"d = 2 ** 4\n" + //
111111
"e = 2.5 ** 3.0\n" + //
112112
"print(a,b,c,d,e)\n";
113-
assertPrints("1.0734391914109869e+17 1.0734391914109868e+18 -821361754992.0 16 15.625\n", source);
113+
assertPrints("1.0734391914109867e+17 1.0734391914109868e+18 -821361754992.0 16 15.625\n", source);
114114
}
115115

116116
@Test

graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_long.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@
1414
*graalpython.lib-python.3.test.test_long.LongTest.test_shift_bool
1515
*graalpython.lib-python.3.test.test_long.LongTest.test_small_ints
1616
*graalpython.lib-python.3.test.test_long.LongTest.test_to_bytes
17+
*graalpython.lib-python.3.test.test_long.LongTest.test_true_division

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,12 @@
4141
package com.oracle.graal.python.builtins.objects.ints;
4242

4343
import static com.oracle.graal.python.nodes.SpecialMethodNames.__LT__;
44+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.OverflowError;
4445
import static com.oracle.graal.python.runtime.exception.PythonErrorType.ValueError;
4546

47+
import java.math.BigDecimal;
4648
import java.math.BigInteger;
49+
import java.math.RoundingMode;
4750
import java.util.List;
4851

4952
import com.oracle.graal.python.PythonLanguage;
@@ -73,6 +76,7 @@
7376
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
7477
import com.oracle.graal.python.nodes.ErrorMessages;
7578
import com.oracle.graal.python.nodes.PGuards;
79+
import com.oracle.graal.python.nodes.PRaiseNode;
7680
import com.oracle.graal.python.nodes.SpecialMethodNames;
7781
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
7882
import com.oracle.graal.python.nodes.call.special.LookupAndCallVarargsNode;
@@ -324,37 +328,49 @@ public abstract static class TrueDivNode extends PythonBinaryBuiltinNode {
324328
if (right.isZero()) {
325329
throw raise(PythonErrorType.ZeroDivisionError, ErrorMessages.DIVISION_BY_ZERO);
326330
}
327-
return op(PInt.longToBigInteger(left), right.getValue());
331+
return op(PInt.longToBigInteger(left), right.getValue(), getRaiseNode());
328332
}
329333

330334
@Specialization
331335
double doPL(PInt left, long right) {
332336
if (right == 0) {
333337
throw raise(PythonErrorType.ZeroDivisionError, ErrorMessages.DIVISION_BY_ZERO);
334338
}
335-
return op(left.getValue(), PInt.longToBigInteger(right));
339+
return op(left.getValue(), PInt.longToBigInteger(right), getRaiseNode());
336340
}
337341

338342
@Specialization
339343
double doPP(PInt left, PInt right) {
340344
if (right.isZero()) {
341345
throw raise(PythonErrorType.ZeroDivisionError, ErrorMessages.DIVISION_BY_ZERO);
342346
}
343-
return op(left.getValue(), right.getValue());
347+
return op(left.getValue(), right.getValue(), getRaiseNode());
344348
}
345349

346350
/*
347351
* We must take special care to do double conversion late (if possible), to avoid loss of
348352
* precision.
349353
*/
350354
@TruffleBoundary
351-
private static double op(BigInteger a, BigInteger b) {
352-
BigInteger[] divideAndRemainder = a.divideAndRemainder(b);
353-
if (divideAndRemainder[1].equals(BigInteger.ZERO)) {
354-
return divideAndRemainder[0].doubleValue();
355-
} else {
355+
private static double op(BigInteger a, BigInteger b, PRaiseNode raiseNode) {
356+
final int precisionOfDouble = 17;
357+
if (fitsIntoDouble(a) && fitsIntoDouble(b)) {
356358
return a.doubleValue() / b.doubleValue();
357359
}
360+
BigDecimal aDecimal = new BigDecimal(a);
361+
BigDecimal bDecimal = new BigDecimal(b);
362+
int aPrec = aDecimal.precision();
363+
int bPrec = bDecimal.precision();
364+
BigDecimal result = aDecimal.divide(bDecimal, bPrec - aPrec + precisionOfDouble, RoundingMode.HALF_EVEN);
365+
double d = result.doubleValue();
366+
if (Double.isInfinite(d)) {
367+
throw raiseNode.raise(OverflowError, ErrorMessages.INTEGER_DIVISION_RESULT_TOO_LARGE);
368+
}
369+
return d;
370+
}
371+
372+
private static boolean fitsIntoDouble(BigInteger x) {
373+
return x.bitLength() < 53;
358374
}
359375

360376
@SuppressWarnings("unused")
@@ -724,7 +740,7 @@ PInt doPLPos(PInt left, long right, @SuppressWarnings("unused") PNone none) {
724740

725741
@Specialization(guards = "right < 0")
726742
double doPLNeg(PInt left, long right, @SuppressWarnings("unused") PNone none) {
727-
return TrueDivNode.op(BigInteger.ONE, op(left.getValue(), -right));
743+
return TrueDivNode.op(BigInteger.ONE, op(left.getValue(), -right), getRaiseNode());
728744
}
729745

730746
@Specialization

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ public abstract class ErrorMessages {
246246
public static final String INT_CANT_CONVERT_STRING_EITH_EXPL_BASE = "int() can't convert non-string with explicit base";
247247
public static final String INT_TOO_LARGE_TO_CONVERT_TO_FLOAT = "int too large to convert to float";
248248
public static final String INTEGER_DIVISION_BY_ZERO = "ZeroDivisionError: integer division or modulo by zero";
249+
public static final String INTEGER_DIVISION_RESULT_TOO_LARGE = "integer division result too large for a float";
249250
public static final String INTEGER_EXPECTED_GOT_FLOAT = "integer argument expected, got float";
250251
public static final String INTEGER_GREATER_THAN_MAX = "integer is greater than maximum";
251252
public static final String INTEGER_IS_REQUIRED = "an integer is required";

0 commit comments

Comments
 (0)