Skip to content

Commit e130d3d

Browse files
committed
[GR-23228] Make a few test_float test pass (fixes pow, mod, round and is_integer)
PullRequest: graalpython/1144
2 parents 5bb6e14 + 6d4824a commit e130d3d

File tree

4 files changed

+101
-37
lines changed

4 files changed

+101
-37
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,19 @@
55
*graalpython.lib-python.3.test.test_float.FormatTestCase.test_issue35560
66
*graalpython.lib-python.3.test.test_float.FormatTestCase.test_issue5864
77
*graalpython.lib-python.3.test.test_float.GeneralFloatCases.test_float_containment
8+
*graalpython.lib-python.3.test.test_float.GeneralFloatCases.test_float_mod
9+
*graalpython.lib-python.3.test.test_float.GeneralFloatCases.test_float_pow
810
*graalpython.lib-python.3.test.test_float.GeneralFloatCases.test_floatasratio
11+
*graalpython.lib-python.3.test.test_float.GeneralFloatCases.test_is_integer
912
*graalpython.lib-python.3.test.test_float.GeneralFloatCases.test_keyword_args
1013
*graalpython.lib-python.3.test.test_float.HexFloatTestCase.test_ends
1114
*graalpython.lib-python.3.test.test_float.HexFloatTestCase.test_from_hex
1215
*graalpython.lib-python.3.test.test_float.HexFloatTestCase.test_invalid_inputs
1316
*graalpython.lib-python.3.test.test_float.HexFloatTestCase.test_roundtrip
1417
*graalpython.lib-python.3.test.test_float.HexFloatTestCase.test_subclass
1518
*graalpython.lib-python.3.test.test_float.HexFloatTestCase.test_whitespace
19+
*graalpython.lib-python.3.test.test_float.IEEEFormatTestCase.test_double_specials_do_unpack
20+
*graalpython.lib-python.3.test.test_float.IEEEFormatTestCase.test_float_specials_do_unpack
1621
*graalpython.lib-python.3.test.test_float.InfNanTest.test_inf_as_str
1722
*graalpython.lib-python.3.test.test_float.InfNanTest.test_inf_from_str
1823
*graalpython.lib-python.3.test.test_float.InfNanTest.test_inf_signs
@@ -21,7 +26,12 @@
2126
*graalpython.lib-python.3.test.test_float.InfNanTest.test_nan_signs
2227
*graalpython.lib-python.3.test.test_float.ReprTestCase.test_repr
2328
*graalpython.lib-python.3.test.test_float.ReprTestCase.test_short_repr
29+
*graalpython.lib-python.3.test.test_float.RoundTestCase.test_None_ndigits
30+
*graalpython.lib-python.3.test.test_float.RoundTestCase.test_inf_nan
31+
*graalpython.lib-python.3.test.test_float.RoundTestCase.test_large_n
2432
*graalpython.lib-python.3.test.test_float.RoundTestCase.test_matches_float_format
33+
*graalpython.lib-python.3.test.test_float.RoundTestCase.test_overflow
2534
*graalpython.lib-python.3.test.test_float.RoundTestCase.test_previous_round_bugs
35+
*graalpython.lib-python.3.test.test_float.RoundTestCase.test_small_n
2636
*graalpython.lib-python.3.test.test_float.UnknownFormatTestCase.test_double_specials_dont_unpack
2737
*graalpython.lib-python.3.test.test_float.UnknownFormatTestCase.test_float_specials_dont_unpack

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
9696
import com.oracle.graal.python.builtins.objects.complex.PComplex;
9797
import com.oracle.graal.python.builtins.objects.dict.PDict;
98+
import com.oracle.graal.python.builtins.objects.floats.FloatBuiltins;
9899
import com.oracle.graal.python.builtins.objects.floats.PFloat;
99100
import com.oracle.graal.python.builtins.objects.frame.PFrame;
100101
import com.oracle.graal.python.builtins.objects.function.PArguments;
@@ -519,8 +520,11 @@ public PTuple doLongZero(long a, long b) {
519520

520521
@Specialization
521522
public PTuple doDouble(double a, double b) {
523+
if (b == 0) {
524+
throw raise(PythonErrorType.ZeroDivisionError, ErrorMessages.DIVISION_BY_ZERO);
525+
}
522526
double q = Math.floor(a / b);
523-
return factory().createTuple(new Object[]{q, a % b});
527+
return factory().createTuple(new Object[]{q, FloatBuiltins.ModNode.op(a, b)});
524528
}
525529

526530
@Specialization
@@ -1739,7 +1743,7 @@ private static String newString(byte[] bytes) {
17391743
}
17401744

17411745
// round(number[, ndigits])
1742-
@Builtin(name = ROUND, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2)
1746+
@Builtin(name = ROUND, minNumOfPositionalArgs = 1, parameterNames = {"number", "ndigits"})
17431747
@GenerateNodeFactory
17441748
public abstract static class RoundNode extends PythonBuiltinNode {
17451749
@Specialization

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

Lines changed: 84 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -458,9 +458,9 @@ private double doSpecialCases(double left, double right, BranchProfile negativeR
458458
// v**(+/-)inf is 1.0 if abs(v) == 1, unlike on Java
459459
return 1;
460460
}
461-
if (left == 0 && right < 0) {
461+
if (left == 0 && right < 0 && Double.isFinite(right)) {
462462
negativeRaise.enter();
463-
// 0**w is an error if w is negative, unlike Java
463+
// 0**w is an error if w is finite and negative, unlike Java
464464
throw raise(PythonBuiltinClassType.ZeroDivisionError, ErrorMessages.POW_ZERO_CANNOT_RAISE_TO_NEGATIVE_POWER);
465465
}
466466
return 0;
@@ -480,7 +480,7 @@ private double doOperation(double left, double right, BranchProfile negativeRais
480480
if (doSpecialCases(left, right, negativeRaise) == 1) {
481481
return 1.0;
482482
}
483-
if (left < 0 && (right % 1 != 0)) {
483+
if (left < 0 && Double.isFinite(left) && Double.isFinite(right) && (right % 1 != 0)) {
484484
CompilerDirectives.transferToInterpreterAndInvalidate();
485485
// Negative numbers raised to fractional powers become complex.
486486
throw new UnexpectedResultException(callPow.execute(frame, factory().createComplex(left, 0), factory().createComplex(right, 0), none));
@@ -495,7 +495,7 @@ Object doDDToComplex(VirtualFrame frame, double left, double right, PNone none,
495495
if (doSpecialCases(left, right, negativeRaise) == 1) {
496496
return 1.0;
497497
}
498-
if (left < 0 && (right % 1 != 0)) {
498+
if (left < 0 && Double.isFinite(left) && Double.isFinite(right) && (right % 1 != 0)) {
499499
// Negative numbers raised to fractional powers become complex.
500500
return callPow.execute(frame, factory().createComplex(left, 0), factory().createComplex(right, 0), none);
501501
}
@@ -608,19 +608,19 @@ abstract static class DivModNode extends FloatBinaryBuiltinNode {
608608
@Specialization
609609
PTuple doDL(double left, long right) {
610610
raiseDivisionByZero(right == 0);
611-
return factory().createTuple(new Object[]{Math.floor(left / right), left % right});
611+
return factory().createTuple(new Object[]{Math.floor(left / right), ModNode.op(left, right)});
612612
}
613613

614614
@Specialization
615615
PTuple doDD(double left, double right) {
616616
raiseDivisionByZero(right == 0.0);
617-
return factory().createTuple(new Object[]{Math.floor(left / right), left % right});
617+
return factory().createTuple(new Object[]{Math.floor(left / right), ModNode.op(left, right)});
618618
}
619619

620620
@Specialization
621621
PTuple doLD(long left, double right) {
622622
raiseDivisionByZero(right == 0.0);
623-
return factory().createTuple(new Object[]{Math.floor(left / right), left % right});
623+
return factory().createTuple(new Object[]{Math.floor(left / right), ModNode.op(left, right)});
624624
}
625625

626626
@Specialization(guards = {"accepts(left)", "accepts(right)"})
@@ -820,42 +820,54 @@ public String hexD(double value) {
820820
@Builtin(name = __MOD__, minNumOfPositionalArgs = 2)
821821
@TypeSystemReference(PythonArithmeticTypes.class)
822822
@GenerateNodeFactory
823-
abstract static class ModNode extends FloatBinaryBuiltinNode {
823+
public abstract static class ModNode extends FloatBinaryBuiltinNode {
824824
@Specialization
825825
double doDL(double left, long right) {
826826
raiseDivisionByZero(right == 0);
827-
return left % right;
827+
return op(left, right);
828828
}
829829

830830
@Specialization
831831
double doDL(double left, PInt right) {
832832
raiseDivisionByZero(right.isZero());
833-
return left % right.doubleValue();
833+
return op(left, right.doubleValue());
834834
}
835835

836836
@Specialization
837837
double doDD(double left, double right) {
838838
raiseDivisionByZero(right == 0.0);
839-
return left % right;
839+
return op(left, right);
840840
}
841841

842842
@Specialization
843843
double doLD(long left, double right) {
844844
raiseDivisionByZero(right == 0.0);
845-
return left % right;
845+
return op(left, right);
846846
}
847847

848848
@Specialization
849849
double doPiD(PInt left, double right) {
850850
raiseDivisionByZero(right == 0.0);
851-
return left.doubleValue() % right;
851+
return op(left.doubleValue(), right);
852852
}
853853

854854
@SuppressWarnings("unused")
855855
@Fallback
856856
PNotImplemented doGeneric(Object right, Object left) {
857857
return PNotImplemented.NOT_IMPLEMENTED;
858858
}
859+
860+
public static double op(double left, double right) {
861+
double mod = left % right;
862+
if (mod != 0.0) {
863+
if ((right < 0) != (mod < 0)) {
864+
mod += right;
865+
}
866+
} else {
867+
mod = right < 0 ? -0.0 : 0.0;
868+
}
869+
return mod;
870+
}
859871
}
860872

861873
@Builtin(name = __RTRUEDIV__, minNumOfPositionalArgs = 2, reverseOperation = true)
@@ -914,41 +926,63 @@ abstract static class RoundNode extends PythonBinaryBuiltinNode {
914926
* The logic is borrowed from Jython.
915927
*/
916928
@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+
917950
@Specialization
918951
double round(double x, long n) {
919952
if (Double.isNaN(x) || Double.isInfinite(x) || x == 0.0) {
920953
// nans, infinities and zeros round to themselves
921954
return x;
922-
} else {
923-
// (Slightly less than) n*log2(10).
924-
float nlog2_10 = 3.3219f * n;
925-
926-
// x = a * 2^b and a<2.
927-
int b = Math.getExponent(x);
928-
929-
if (nlog2_10 > 52 - b) {
930-
// When n*log2(10) > nmax, the lsb of abs(x) is >1, so x rounds to itself.
931-
return x;
932-
} else if (nlog2_10 < -(b + 2)) {
933-
// When n*log2(10) < -(b+2), abs(x)<0.5*10^n so x rounds to (signed) zero.
934-
return Math.copySign(0.0, x);
935-
} else {
936-
// We have to work it out properly.
937-
BigDecimal xx = BigDecimal.valueOf(x);
938-
BigDecimal rr = xx.setScale((int) n, RoundingMode.HALF_UP);
939-
return rr.doubleValue();
940-
}
941955
}
956+
double d = op(x, n);
957+
if (Double.isInfinite(d)) {
958+
throw raise(OverflowError, ErrorMessages.ROUNDED_VALUE_TOO_LARGE);
959+
}
960+
return d;
942961
}
943962

944-
@TruffleBoundary
945963
@Specialization
946964
double round(double x, PInt n) {
947-
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);
948974
}
949975

950976
@Specialization
951-
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+
}
952986
return (long) round(x, 0);
953987
}
954988

@@ -1541,4 +1575,19 @@ protected void raiseDivisionByZero(boolean cond) {
15411575
}
15421576
}
15431577
}
1578+
1579+
@Builtin(name = "is_integer", minNumOfPositionalArgs = 1)
1580+
@GenerateNodeFactory
1581+
abstract static class IsIntegerNode extends PythonUnaryBuiltinNode {
1582+
@Specialization
1583+
boolean isInteger(double value) {
1584+
return Double.isFinite(value) && (long) value == value;
1585+
}
1586+
1587+
@Specialization
1588+
boolean trunc(PFloat pValue) {
1589+
return isInteger(pValue.getValue());
1590+
}
1591+
1592+
}
15441593
}

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)