Skip to content

Commit 484fcf9

Browse files
committed
math: powf: make a precision vs size trade-off for flash-constrained builds
.. by calculating x**y as exp(y*log(x)) after some suitable initial tests. The most notable anomaly I found is that powf() is used in number parsing and it changes from the correct >>> float("10000000") - float("1e7") 0.0 to the incorrect >>> float(10000000) - float("1e7") 4.0 Size savings is 1352 bytes on Trinket M0, and the existing more accurate behavior is preserved on boards with CIRCUITPY_FULL_BUILD. I investigated whether "exponentiation by squaring" gave more accurate results for integer exponents, but it did not. New behavior: ``` >>> sum( (-1.1) ** x for x in range(-6, 6)) -0.574803 >>> sum( 10. ** y for y in range(-6, 6)) 111111.0 >>> sum( y ** (4/3) for y in range(-6, 6) ) # Complex values not supported Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 1, in <genexpr> ValueError: complex values not supported >>> sum( y ** (-4/3) for y in range(1, 6) ) 1.90242 ``` Original behavior: ``` >>> sum( (-1.1) ** x for x in range(-6, 6)) -0.574803 >>> sum( 10. ** y for y in range(-6, 6)) 111111.0 >>> sum( y ** (4/3) for y in range(-6, 6) ) # Complex values not supported Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 1, in <genexpr> ValueError: complex values not supported >>> sum( y ** (-4/3) for y in range(1, 6) ) 1.90242 ``` Desktop Python behavior: ``` >>> sum( (-1.1) ** x for x in range(-6, 6)) -0.574803366641059 >>> sum( 10. ** y for y in range(-6, 6)) 111111.111111 >>> sum( y ** (4./3) for y in range(-6, 6) ) # Complex values not supported (5.921675597487694-29.140714142379153j) >>> sum( y ** (-4./3) for y in range(1, 6) ) 1.9024215285409685 ```
1 parent 354edd9 commit 484fcf9

File tree

1 file changed

+85
-0
lines changed

1 file changed

+85
-0
lines changed

lib/libm/math.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ float scalbnf(float x, int n)
111111
return x;
112112
}
113113

114+
#if CIRCUITPY_FULL_BUILD
114115
/*****************************************************************************/
115116
/*****************************************************************************/
116117
// powf from musl-0.9.15
@@ -374,6 +375,90 @@ float powf(float x, float y)
374375
SET_FLOAT_WORD(z, j);
375376
return sn*z;
376377
}
378+
#else
379+
float powf(float x, float y) {
380+
float z,ax;
381+
float sn;
382+
int32_t j,k,yisint;
383+
int32_t hx,hy,ix,iy;
384+
385+
GET_FLOAT_WORD(hx, x);
386+
GET_FLOAT_WORD(hy, y);
387+
ix = hx & 0x7fffffff;
388+
iy = hy & 0x7fffffff;
389+
390+
/* x**0 = 1, even if x is NaN */
391+
if (iy == 0)
392+
return 1.0f;
393+
/* 1**y = 1, even if y is NaN */
394+
if (hx == 0x3f800000)
395+
return 1.0f;
396+
/* NaN if either arg is NaN */
397+
if (ix > 0x7f800000 || iy > 0x7f800000)
398+
return x + y;
399+
400+
/* determine if y is an odd int when x < 0
401+
* yisint = 0 ... y is not an integer
402+
* yisint = 1 ... y is an odd int
403+
* yisint = 2 ... y is an even int
404+
*/
405+
yisint = 0;
406+
if (hx < 0) {
407+
if (iy >= 0x4b800000)
408+
yisint = 2; /* even integer y */
409+
else if (iy >= 0x3f800000) {
410+
k = (iy>>23) - 0x7f; /* exponent */
411+
j = iy>>(23-k);
412+
if ((j<<(23-k)) == iy)
413+
yisint = 2 - (j & 1);
414+
}
415+
}
416+
417+
/* special value of y */
418+
if (iy == 0x7f800000) { /* y is +-inf */
419+
if (ix == 0x3f800000) /* (-1)**+-inf is 1 */
420+
return 1.0f;
421+
else if (ix > 0x3f800000) /* (|x|>1)**+-inf = inf,0 */
422+
return hy >= 0 ? y : 0.0f;
423+
else if (ix != 0) /* (|x|<1)**+-inf = 0,inf if x!=0 */
424+
return hy >= 0 ? 0.0f: -y;
425+
}
426+
if (iy == 0x3f800000) /* y is +-1 */
427+
return hy >= 0 ? x : 1.0f/x;
428+
if (hy == 0x40000000) /* y is 2 */
429+
return x*x;
430+
if (hy == 0x3f000000) { /* y is 0.5 */
431+
if (hx >= 0) /* x >= +0 */
432+
return sqrtf(x);
433+
}
434+
435+
ax = fabsf(x);
436+
/* special value of x */
437+
if (ix == 0x7f800000 || ix == 0 || ix == 0x3f800000) { /* x is +-0,+-inf,+-1 */
438+
z = ax;
439+
if (hy < 0) /* z = (1/|x|) */
440+
z = 1.0f/z;
441+
if (hx < 0) {
442+
if (((ix-0x3f800000)|yisint) == 0) {
443+
z = (z-z)/(z-z); /* (-1)**non-int is NaN */
444+
} else if (yisint == 1)
445+
z = -z; /* (x<0)**odd = -(|x|**odd) */
446+
}
447+
return z;
448+
}
449+
450+
sn = 1.0f; /* sign of result */
451+
if (hx < 0) {
452+
if (yisint == 0) /* (x<0)**(non-int) is NaN */
453+
return (x-x)/(x-x);
454+
if (yisint == 1) /* (x<0)**(odd int) */
455+
sn = -1.0f;
456+
}
457+
458+
return sn * expf(y * logf(ax));
459+
}
460+
#endif
461+
377462

378463
/*****************************************************************************/
379464
/*****************************************************************************/

0 commit comments

Comments
 (0)