From 7fd7e2db152c12e5ada1c3e8222d55ecf8b983cb Mon Sep 17 00:00:00 2001 From: Md Kaif <119096690+LordAizen1@users.noreply.github.com> Date: Tue, 21 Jul 2026 17:06:08 +0530 Subject: [PATCH] fix(float): abs(-0.0) should return 0.0 (#534) abs(-0.0) returns -0.0 instead of 0.0, and math.fabs(-0.0) does the same. Both come from the idiom (x < 0) ? -x : x. Since -0.0 < 0 is false, the negative zero is returned unchanged. The idiom appears twice, once in dmath_fabs and once open-coded in float__abs__. Fix dmath_fabs by clearing the sign bit, the same way dmath_copysign just above it works, and make float__abs__ call the shared helper instead of repeating the comparison. abs(-0.0) == 0.0 is true even with the bug, because -0.0 == 0.0 under IEEE 754, so the added tests compare str(...) to check the sign. --- src/bindings/py_number.c | 2 +- src/common/dmath.c | 5 ++++- tests/020_float.py | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/bindings/py_number.c b/src/bindings/py_number.c index 7aeb49b6..683fee1f 100644 --- a/src/bindings/py_number.c +++ b/src/bindings/py_number.c @@ -384,7 +384,7 @@ static bool int__abs__(int argc, py_Ref argv) { static bool float__abs__(int argc, py_Ref argv) { PY_CHECK_ARGC(1); py_f64 val = py_tofloat(&argv[0]); - py_newfloat(py_retval(), val < 0 ? -val : val); + py_newfloat(py_retval(), dmath_fabs(val)); return true; } diff --git a/src/common/dmath.c b/src/common/dmath.c index e587fdc3..fdcf4a38 100644 --- a/src/common/dmath.c +++ b/src/common/dmath.c @@ -701,8 +701,11 @@ double dmath_copysign(double x, double y) { return ux.f; } +// https://github.com/kraj/musl/blob/kraj/master/src/math/fabs.c double dmath_fabs(double x) { - return (x < 0) ? -x : x; + union Float64Bits u = { .f = x }; + u.i &= -1ULL/2; + return u.f; } double dmath_ceil(double x) { diff --git a/tests/020_float.py b/tests/020_float.py index 4652133a..9fa080ad 100644 --- a/tests/020_float.py +++ b/tests/020_float.py @@ -97,6 +97,9 @@ assert 3.4e+3 == 3400.0 assert abs(1.0) == 1.0 assert abs(-1.0) == 1.0 assert abs(0.0) == 0.0 +# abs(-0.0) is 0.0, not -0.0. `==` cannot tell them apart, so check the sign. +assert str(abs(-0.0)) == '0.0' +assert str(abs(0.0)) == '0.0' # import math # assert math.isnan(0/0)