Skip to content

Commit e0d03ad

Browse files
committed
Fixed corner-cases of float.__round__()
1 parent e6966ff commit e0d03ad

File tree

3 files changed

+46
-23
lines changed

3 files changed

+46
-23
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1743,7 +1743,7 @@ private static String newString(byte[] bytes) {
17431743
}
17441744

17451745
// round(number[, ndigits])
1746-
@Builtin(name = ROUND, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2)
1746+
@Builtin(name = ROUND, minNumOfPositionalArgs = 1, parameterNames = {"number", "ndigits"})
17471747
@GenerateNodeFactory
17481748
public abstract static class RoundNode extends PythonBuiltinNode {
17491749
@Specialization

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/FloatBuiltins.java

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -926,41 +926,63 @@ abstract static class RoundNode extends PythonBinaryBuiltinNode {
926926
* The logic is borrowed from Jython.
927927
*/
928928
@TruffleBoundary
929+
private static double op(double x, long n) {
930+
// (Slightly less than) n*log2(10).
931+
float nlog2_10 = 3.3219f * n;
932+
933+
// x = a * 2^b and a<2.
934+
int b = Math.getExponent(x);
935+
936+
if (nlog2_10 > 52 - b) {
937+
// When n*log2(10) > nmax, the lsb of abs(x) is >1, so x rounds to itself.
938+
return x;
939+
} else if (nlog2_10 < -(b + 2)) {
940+
// When n*log2(10) < -(b+2), abs(x)<0.5*10^n so x rounds to (signed) zero.
941+
return Math.copySign(0.0, x);
942+
} else {
943+
// We have to work it out properly.
944+
BigDecimal xx = BigDecimal.valueOf(x);
945+
BigDecimal rr = xx.setScale((int) n, RoundingMode.HALF_UP);
946+
return rr.doubleValue();
947+
}
948+
}
949+
929950
@Specialization
930951
double round(double x, long n) {
931952
if (Double.isNaN(x) || Double.isInfinite(x) || x == 0.0) {
932953
// nans, infinities and zeros round to themselves
933954
return x;
934-
} else {
935-
// (Slightly less than) n*log2(10).
936-
float nlog2_10 = 3.3219f * n;
937-
938-
// x = a * 2^b and a<2.
939-
int b = Math.getExponent(x);
940-
941-
if (nlog2_10 > 52 - b) {
942-
// When n*log2(10) > nmax, the lsb of abs(x) is >1, so x rounds to itself.
943-
return x;
944-
} else if (nlog2_10 < -(b + 2)) {
945-
// When n*log2(10) < -(b+2), abs(x)<0.5*10^n so x rounds to (signed) zero.
946-
return Math.copySign(0.0, x);
947-
} else {
948-
// We have to work it out properly.
949-
BigDecimal xx = BigDecimal.valueOf(x);
950-
BigDecimal rr = xx.setScale((int) n, RoundingMode.HALF_UP);
951-
return rr.doubleValue();
952-
}
953955
}
956+
double d = op(x, n);
957+
if (Double.isInfinite(d)) {
958+
throw raise(OverflowError, ErrorMessages.ROUNDED_VALUE_TOO_LARGE);
959+
}
960+
return d;
954961
}
955962

956-
@TruffleBoundary
957963
@Specialization
958964
double round(double x, PInt n) {
959-
return round(x, n.longValue());
965+
long nLong;
966+
if (n.compareTo(Long.MAX_VALUE) > 0) {
967+
nLong = Long.MAX_VALUE;
968+
} else if (n.compareTo(Long.MIN_VALUE) < 0) {
969+
nLong = Long.MIN_VALUE;
970+
} else {
971+
nLong = n.longValue();
972+
}
973+
return round(x, nLong);
960974
}
961975

962976
@Specialization
963-
long round(double x, @SuppressWarnings("unused") PNone none) {
977+
long round(double x, @SuppressWarnings("unused") PNone none,
978+
@Cached("createBinaryProfile()") ConditionProfile nanProfile,
979+
@Cached("createBinaryProfile()") ConditionProfile infProfile) {
980+
if (nanProfile.profile(Double.isNaN(x))) {
981+
throw raise(PythonErrorType.ValueError, ErrorMessages.CANNOT_CONVERT_S_TO_INT, "float NaN");
982+
}
983+
if (infProfile.profile(Double.isInfinite(x))) {
984+
throw raise(PythonErrorType.OverflowError, ErrorMessages.CANNOT_CONVERT_S_TO_INT, "float infinity");
985+
}
964986
return (long) round(x, 0);
965987
}
966988

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
@@ -417,6 +417,7 @@ public abstract class ErrorMessages {
417417
public static final String READONLY_ATTRIBUTE = "readonly attribute";
418418
public static final String REQUIRES_CODE_OBJ = "%s() requires a code object with %d free vars, not %d";
419419
public static final String REQUIRES_INT_OR_CHAR = "%%%c requires int or char";
420+
public static final String ROUNDED_VALUE_TOO_LARGE = "rounded value too large to represent";
420421
public static final String S_FORMAT_NUMBER_IS_REQUIRED_NOT_S = "%%%s format: a number is required, not %p";
421422
public static final String S_FORMAT_INTEGER_IS_REQUIRED_NOT_S = "%%%s format: an integer is required, not %p";
422423
public static final String C_ARG_NOT_IN_RANGE256_DECIMAL = "%%c arg not in range(256)";

0 commit comments

Comments
 (0)