From aa2679b6b0bbbffcb454081a81346c0a82804e52 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 22 Mar 2023 17:35:34 +0000 Subject: [PATCH] [mypyc] Fixes to float to int conversion (#14936) Fix undefined behavior due to converting a negative float to an unsigned integer type. Fix edge cases on 32-bit platforms. The first issue was caught by the testI32BasicOps test case, but only in some configurations, it looks like. --- mypyc/lib-rt/int_ops.c | 4 ++-- mypyc/test-data/run-floats.test | 10 ++++++++++ test-data/unit/lib-stub/math.pyi | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 48583b056b83..843d9b0d2230 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -294,8 +294,8 @@ PyObject *CPyLong_FromStr(PyObject *o) { } CPyTagged CPyTagged_FromFloat(double f) { - if (f < (double)CPY_TAGGED_MAX && f > CPY_TAGGED_MIN) { - return (CPyTagged)f << 1; + if (f < ((double)CPY_TAGGED_MAX + 1.0) && f > (CPY_TAGGED_MIN - 1.0)) { + return (Py_ssize_t)f << 1; } PyObject *o = PyLong_FromDouble(f); if (o == NULL) diff --git a/mypyc/test-data/run-floats.test b/mypyc/test-data/run-floats.test index d84681ec658e..2c101100549d 100644 --- a/mypyc/test-data/run-floats.test +++ b/mypyc/test-data/run-floats.test @@ -185,6 +185,16 @@ def test_explicit_conversion_to_int() -> None: else: assert repr(int(x)) == repr(int_any(x)) + # Test some edge cases + assert 2**30 == int(2.0**30 + int()) + assert 2**30 - 1 == int(1073741823.9999999 + int()) # math.nextafter(2.0**30, 0)) + assert -2**30 - 1 == int(-2.0**30 - 1 + int()) + assert -2**30 == int(-1073741824.9999998 + int()) # math.nextafter(-2.0**30 - 1, 0) + assert 2**62 == int(2.0**62 + int()) + assert 2**62 == int(2.0**62 - 1 + int()) + assert -2**62 == int(-2.0**62 + int()) + assert -2**62 == int(-2.0**62 - 1 + int()) + def str_to_float(x: str) -> float: return float(x) diff --git a/test-data/unit/lib-stub/math.pyi b/test-data/unit/lib-stub/math.pyi index 85f3b3f169e1..587b04a56de8 100644 --- a/test-data/unit/lib-stub/math.pyi +++ b/test-data/unit/lib-stub/math.pyi @@ -13,3 +13,4 @@ def copysign(__x: float, __y: float) -> float: ... def isinf(__x: float) -> bool: ... def isnan(__x: float) -> bool: ... def isfinite(__x: float) -> bool: ... +def nextafter(__x: float, __y: float) -> float: ...