Skip to content

Commit 907ada3

Browse files
committed
Improved precision of int.__truediv__
1 parent fa42168 commit 907ada3

File tree

3 files changed

+27
-10
lines changed

3 files changed

+27
-10
lines changed

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
@@ -243,6 +243,7 @@ public abstract class ErrorMessages {
243243
public static final String INT_CANT_CONVERT_STRING_EITH_EXPL_BASE = "int() can't convert non-string with explicit base";
244244
public static final String INT_TOO_LARGE_TO_CONVERT_TO_FLOAT = "int too large to convert to float";
245245
public static final String INTEGER_DIVISION_BY_ZERO = "ZeroDivisionError: integer division or modulo by zero";
246+
public static final String INTEGER_DIVISION_RESULT_TOO_LARGE = "integer division result too large for a float";
246247
public static final String INTEGER_EXPECTED_GOT_FLOAT = "integer argument expected, got float";
247248
public static final String INTEGER_GREATER_THAN_MAX = "integer is greater than maximum";
248249
public static final String INTEGER_IS_REQUIRED = "an integer is required";

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/function/PythonBuiltinBaseNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ protected final PythonObjectFactory factory() {
101101
return objectFactory;
102102
}
103103

104-
private final PRaiseNode getRaiseNode() {
104+
protected final PRaiseNode getRaiseNode() {
105105
if (raiseNode == null) {
106106
CompilerDirectives.transferToInterpreterAndInvalidate();
107107
if (isAdoptable()) {

0 commit comments

Comments
 (0)