Dear Sir/Madam,

As mentioned in here, there is a floating point imprecision problem in the pow function.

Originally,

#include <stdio.h>
#include <math.h>

int main() {
union {
double d;
unsigned long long int i;
} cast;
cast.d = pow(10., -9);
printf("%llx\n", cast.i);
return 0;
}
3e112e0be826d699

the problem should be fixed after modification

3e112e0be826d695

this modification should also pass the special cases

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

double powi_wrong(double d, int y) {
if (y < 0) {
d = 1.0 / d;
y = -y;
}

unsigned int u = (unsigned int) y;
double rslt = ((u & 1) != 0) ? d : 1.0;
u >>= 1;
do {
d *= d;
if ((u & 1) != 0)
rslt *= d;
u >>= 1;
} while (u > 0);
return rslt;
}

double powi_correct(double a, int b) {
const int recip = b < 0;
double r = 1;
while (1) {
if (b & 1)
r *= a;
b /= 2;
if (b == 0)
break;
a *= a;
}
return recip ? 1 / r : r;
}

int main()
{
printf("pow correct:\n%llx\n", powi_correct(10, -9));
printf("pow(10, -9):\n%llx\n", pow(10.0, -9));

printf("\nSpecial cases:\n");
printf("powi (x, +/-0) is 1 for any x (even a zero, quiet NaN, or infinity)\n");
printf("pow(5., 0):\n%f\n", pow(5.,0));

printf("powi (+1, y) is 1 for any y (even a quiet NaN)\n");
printf("pow(1., 0):\n%f\n", pow(1.,0));

printf("powi (+/-0, y) is +/-oo and signals the divideByZero exception for y an odd integer < 0\n");
printf("pow(0., -7):\n%f\n", pow(0.,-7));
printf("pow(-0., -7):\n%f\n", pow(-0.,-7));

printf("powi (+/-0, y) is +oo and signals the divideByZero exception for finite y < 0 and not an odd integer\n");
printf("pow(0., -8):\n%f\n", pow(0., -8));
printf("pow(-0., -8):\n%f\n", pow(-0., -8));

printf("powi (+/-0, y) is +/-0 for finite y > 0 an odd integer\n");
printf("pow(0., 7):\n%f\n", pow(0., 7));
printf("pow(-0., 7):\n%f\n", pow(-0., 7));

printf("powi (+/-0, y) is +0 for finite y > 0 and not an odd integer\n");
printf("pow(0., 8):\n%f\n", pow(0., 8));
printf("pow(-0., 8):\n%f\n", pow(0., 8));

long a = 10;
long b = -9;
double c = pow(a, b);
printf("10**-9 == 1e-9:\n%d\n", c == 1e-9);
printf("10**-9 is:\n%.17le\n", c);
printf("%llx", pow(10, -9));
}

result:

pow inaccurate:
3e112e0be826d699
pow(10, -9):
3e112e0be826d695

Special cases:
powi (x, +/-0) is 1 for any x (even a zero, quiet NaN, or infinity)
pow(5., 0):
1.000000
powi (+1, y) is 1 for any y (even a quiet NaN)
pow(1., 0):
1.000000
powi (+/-0, y) is +/-oo and signals the divideByZero exception for y an odd integer < 0
pow(0., -7):
inf
pow(-0., -7):
-inf
powi (+/-0, y) is +oo and signals the divideByZero exception for finite y < 0 and not an odd integer
pow(0., -8):
inf
pow(-0., -8):
inf
powi (+/-0, y) is +/-0 for finite y > 0 an odd integer
pow(0., 7):
0.000000
pow(-0., 7):
-0.000000
powi (+/-0, y) is +0 for finite y > 0 and not an odd integer
pow(0., 8):
0.000000
pow(-0., 8):
0.000000
10**-9 == 1e-9:
1
10**-9 is:
1.00000000000000006e-09
3e112e0be826d695

Attached is a patch to fix that.

Best,
Gary
From 53482aa782df3405edc952421ce11413063cdb13 Mon Sep 17 00:00:00 2001
From: Gary Wan <[email protected]>
Date: Mon, 6 Jun 2022 11:01:03 +0800
Subject: [PATCH] fix pow() floating point imprecision

---
 mingw-w64-crt/math/powi.def.h | 39 ++++++++++++-----------------------
 1 file changed, 13 insertions(+), 26 deletions(-)

diff --git a/mingw-w64-crt/math/powi.def.h b/mingw-w64-crt/math/powi.def.h
index 1997727fd..249fb64e4 100644
--- a/mingw-w64-crt/math/powi.def.h
+++ b/mingw-w64-crt/math/powi.def.h
@@ -121,33 +121,20 @@ __FLT_ABI(__powi) (__FLT_TYPE x, int y)
       return (odd_y && signbit(x) ? -__FLT_HUGE_VAL : __FLT_HUGE_VAL);
     }
 
-  d = __FLT_ABI(fabs) (x);
 
-  if (y < 0)
-    {
-      d = __FLT_CST(1.0) / d;
-      y = -y;
+    double a = x;
+    int b = y;
+
+    const int recip = b < 0;
+    double r = 1;
+    while (1) {
+        if (b & 1)
+            r *= a;
+        b /= 2;
+        if (b == 0)
+            break;
+        a *= a;
     }
+    return recip ? 1 / r : r;
 
-  if (!y)
-    rslt = __FLT_CST(1.0);
-  else if (y == 1)
-    rslt = d;
-  else
-    {
-      unsigned int u = (unsigned int) y;
-      rslt = ((u & 1) != 0) ? d : __FLT_CST(1.0);
-      u >>= 1;
-      do
-	{
-	  d *= d;
-	  if ((u & 1) != 0)
-	    rslt *= d;
-	  u >>= 1;
-	}
-      while (u > 0);
-    }
-  if (signbit (x) && odd_y)
-    rslt = -rslt;
-  return rslt;
 }
-- 
2.36.0

_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to