Просмотр исходного кода

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.
Md Kaif 3 недель назад
Родитель
Сommit
7fd7e2db15
3 измененных файлов с 8 добавлено и 2 удалено
  1. 1 1
      src/bindings/py_number.c
  2. 4 1
      src/common/dmath.c
  3. 3 0
      tests/020_float.py

+ 1 - 1
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) {
 static bool float__abs__(int argc, py_Ref argv) {
     PY_CHECK_ARGC(1);
     PY_CHECK_ARGC(1);
     py_f64 val = py_tofloat(&argv[0]);
     py_f64 val = py_tofloat(&argv[0]);
-    py_newfloat(py_retval(), val < 0 ? -val : val);
+    py_newfloat(py_retval(), dmath_fabs(val));
     return true;
     return true;
 }
 }
 
 

+ 4 - 1
src/common/dmath.c

@@ -701,8 +701,11 @@ double dmath_copysign(double x, double y) {
 	return ux.f;
 	return ux.f;
 }
 }
 
 
+// https://github.com/kraj/musl/blob/kraj/master/src/math/fabs.c
 double dmath_fabs(double x) {
 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) {
 double dmath_ceil(double x) {

+ 3 - 0
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(-1.0) == 1.0
 assert abs(-1.0) == 1.0
 assert abs(0.0) == 0.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
 # import math
 # assert math.isnan(0/0)
 # assert math.isnan(0/0)