YAHAL
Yet Another Hardware Abstraction Library
Loading...
Searching...
No Matches
double_math.c
1/*
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <math.h>
8//#include "pico/types.h"
9//#include "pico/double.h"
10//#include "pico/platform.h"
11
12#define WRAPPER_FUNC(x) __wrap_ ## x
13#define WRAPPER_FUNC_NAME(x) __wrap_##x
14
15#include <math.h>
16#include <stdint.h>
17#include <stdbool.h>
18
19double fix642double(int64_t m, int e);
20
21// opened a separate issue https://github.com/raspberrypi/pico-sdk/issues/166 to deal with these warnings if at all
22_Pragma("GCC diagnostic push")
23_Pragma("GCC diagnostic ignored \"-Wconversion\"")
24_Pragma("GCC diagnostic ignored \"-Wsign-conversion\"")
25
26typedef uint64_t ui64;
27typedef uint32_t ui32;
28typedef int64_t i64;
29
30#define PINF ( HUGE_VAL)
31#define MINF (-HUGE_VAL)
32#define PZERO (+0.0)
33#define MZERO (-0.0)
34
35
36#define PI 3.14159265358979323846
37#define LOG2 0.69314718055994530941
38// Unfortunately in double precision ln(10) is very close to half-way between to representable numbers
39#define LOG10 2.30258509299404568401
40#define LOG2E 1.44269504088896340737
41#define LOG10E 0.43429448190325182765
42#define ONETHIRD 0.33333333333333333333
43
44#define PIf 3.14159265358979323846f
45#define LOG2f 0.69314718055994530941f
46#define LOG2Ef 1.44269504088896340737f
47#define LOG10Ef 0.43429448190325182765f
48#define ONETHIRDf 0.33333333333333333333f
49
50#define DUNPACK(x,e,m) e=((x)>>52)&0x7ff,m=((x)&0x000fffffffffffffULL)|0x0010000000000000ULL
51#define DUNPACKS(x,s,e,m) s=((x)>>63),DUNPACK((x),(e),(m))
52
53typedef union {
54 double d;
55 ui64 ix;
56} double_ui64;
57
58static inline double ui642double(ui64 ix) {
59 double_ui64 tmp;
60 tmp.ix = ix;
61 return tmp.d;
62}
63
64static inline ui64 double2ui64(double d) {
65 double_ui64 tmp;
66 tmp.d = d;
67 return tmp.ix;
68}
69
70static inline bool disnan(double x) {
71 ui64 ix= double2ui64(x);
72 // checks the top bit of the low 32 bit of the NAN, but it I think that is ok
73 return ((uint32_t)(ix >> 31)) > 0xffe00000u;
74}
75
76#if PICO_DOUBLE_PROPAGATE_NANS
77#define check_nan_d1(x) if (disnan((x))) return (x)
78#define check_nan_d2(x,y) if (disnan((x))) return (x); else if (disnan((y))) return (y);
79#else
80#define check_nan_d1(x) ((void)0)
81#define check_nan_d2(x,y) ((void)0)
82#endif
83
84static inline int dgetsignexp(double x) {
85 ui64 ix=double2ui64(x);
86 return (ix>>52)&0xfff;
87}
88
89static inline int dgetexp(double x) {
90 ui64 ix=double2ui64(x);
91 return (ix>>52)&0x7ff;
92}
93
94static inline double dldexp(double x,int de) {
95 ui64 ix=double2ui64(x),iy;
96 int e;
97 e=dgetexp(x);
98 if(e==0||e==0x7ff) return x;
99 e+=de;
100 if(e<=0) iy=ix&0x8000000000000000ULL; // signed zero for underflow
101 else if(e>=0x7ff) iy=(ix&0x8000000000000000ULL)|0x7ff0000000000000ULL; // signed infinity on overflow
102 else iy=ix+((ui64)de<<52);
103 return ui642double(iy);
104}
105
106double WRAPPER_FUNC(ldexp)(double x, int de) {
107 check_nan_d1(x);
108 return dldexp(x, de);
109}
110
111
112static inline double dcopysign(double x,double y) {
113 ui64 ix=double2ui64(x),iy=double2ui64(y);
114 ix=((ix&0x7fffffffffffffffULL)|(iy&0x8000000000000000ULL));
115 return ui642double(ix);
116}
117
118double WRAPPER_FUNC(copysign)(double x, double y) {
119 check_nan_d2(x,y);
120 return dcopysign(x, y);
121}
122static inline int diszero(double x) { return dgetexp (x)==0; }
123static inline int dispzero(double x) { return dgetsignexp(x)==0; }
124static inline int dismzero(double x) { return dgetsignexp(x)==0x800; }
125static inline int disinf(double x) { return dgetexp (x)==0x7ff; }
126static inline int dispinf(double x) { return dgetsignexp(x)==0x7ff; }
127static inline int disminf(double x) { return dgetsignexp(x)==0xfff; }
128
129static inline int disint(double x) {
130 ui64 ix=double2ui64(x),m;
131 int e=dgetexp(x);
132 if(e==0) return 1; // 0 is an integer
133 e-=0x3ff; // remove exponent bias
134 if(e<0) return 0; // |x|<1
135 e=52-e; // bit position in mantissa with significance 1
136 if(e<=0) return 1; // |x| large, so must be an integer
137 m=(1ULL<<e)-1; // mask for bits of significance <1
138 if(ix&m) return 0; // not an integer
139 return 1;
140}
141
142static inline int disoddint(double x) {
143 ui64 ix=double2ui64(x),m;
144 int e=dgetexp(x);
145 e-=0x3ff; // remove exponent bias
146 if(e<0) return 0; // |x|<1; 0 is not odd
147 e=52-e; // bit position in mantissa with significance 1
148 if(e<0) return 0; // |x| large, so must be even
149 m=(1ULL<<e)-1; // mask for bits of significance <1 (if any)
150 if(ix&m) return 0; // not an integer
151 if(e==52) return 1; // value is exactly 1
152 return (ix>>e)&1;
153}
154
155static inline int disstrictneg(double x) {
156 ui64 ix=double2ui64(x);
157 if(diszero(x)) return 0;
158 return ix>>63;
159}
160
161static inline int disneg(double x) {
162 ui64 ix=double2ui64(x);
163 return ix>>63;
164}
165
166static inline double dneg(double x) {
167 ui64 ix=double2ui64(x);
168 ix^=0x8000000000000000ULL;
169 return ui642double(ix);
170}
171
172static inline int dispo2(double x) {
173 ui64 ix=double2ui64(x);
174 if(diszero(x)) return 0;
175 if(disinf(x)) return 0;
176 ix&=0x000fffffffffffffULL;
177 return ix==0;
178}
179
180static inline double dnan_or(double x) {
181#if PICO_DOUBLE_PROPAGATE_NANS
182 return NAN;
183#else
184 return x;
185#endif
186}
187
188double WRAPPER_FUNC(trunc)(double x) {
189 check_nan_d1(x);
190 ui64 ix=double2ui64(x),m;
191 int e=dgetexp(x);
192 e-=0x3ff; // remove exponent bias
193 if(e<0) { // |x|<1
194 ix&=0x8000000000000000ULL;
195 return ui642double(ix);
196 }
197 e=52-e; // bit position in mantissa with significance 1
198 if(e<=0) return x; // |x| large, so must be an integer
199 m=(1ULL<<e)-1; // mask for bits of significance <1
200 ix&=~m;
201 return ui642double(ix);
202}
203
204double WRAPPER_FUNC(round)(double x) {
205 check_nan_d1(x);
206 ui64 ix=double2ui64(x),m;
207 int e=dgetexp(x);
208 e-=0x3ff; // remove exponent bias
209 if(e<-1) { // |x|<0.5
210 ix&=0x8000000000000000ULL;
211 return ui642double(ix);
212 }
213 if(e==-1) { // 0.5<=|x|<1
214 ix&=0x8000000000000000ULL;
215 ix|=0x3ff0000000000000ULL; // ±1
216 return ui642double(ix);
217 }
218 e=52-e; // bit position in mantissa with significance 1, <=52
219 if(e<=0) return x; // |x| large, so must be an integer
220 m=1ULL<<(e-1); // mask for bit of significance 0.5
221 ix+=m;
222 m=m+m-1; // mask for bits of significance <1
223 ix&=~m;
224 return ui642double(ix);
225}
226
227double WRAPPER_FUNC(floor)(double x) {
228 check_nan_d1(x);
229 ui64 ix=double2ui64(x),m;
230 int e=dgetexp(x);
231 if(e==0) { // x==0
232 ix&=0x8000000000000000ULL;
233 return ui642double(ix);
234 }
235 e-=0x3ff; // remove exponent bias
236 if(e<0) { // |x|<1, not zero
237 if(disneg(x)) return -1;
238 return PZERO;
239 }
240 e=52-e; // bit position in mantissa with significance 1
241 if(e<=0) return x; // |x| large, so must be an integer
242 m=(1ULL<<e)-1; // mask for bit of significance <1
243 if(disneg(x)) ix+=m; // add 1-ε to magnitude if negative
244 ix&=~m; // truncate
245 return ui642double(ix);
246}
247
248double WRAPPER_FUNC(ceil)(double x) {
249 check_nan_d1(x);
250 ui64 ix=double2ui64(x),m;
251 int e=dgetexp(x);
252 if(e==0) { // x==0
253 ix&=0x8000000000000000ULL;
254 return ui642double(ix);
255 }
256 e-=0x3ff; // remove exponent bias
257 if(e<0) { // |x|<1, not zero
258 if(disneg(x)) return MZERO;
259 return 1;
260 }
261 e=52-e; // bit position in mantissa with significance 1
262 if(e<=0) return x; // |x| large, so must be an integer
263 m=(1ULL<<e)-1; // mask for bit of significance <1
264 if(!disneg(x)) ix+=m; // add 1-ε to magnitude if positive
265 ix&=~m; // truncate
266 return ui642double(ix);
267}
268
269double WRAPPER_FUNC(asin)(double x) {
270 check_nan_d1(x);
271 double u;
272 u=(1-x)*(1+x);
273 if(disstrictneg(u)) return dnan_or(PINF);
274 return atan2(x,sqrt(u));
275}
276
277double WRAPPER_FUNC(acos)(double x) {
278 check_nan_d1(x);
279 double u;
280 u=(1-x)*(1+x);
281 if(disstrictneg(u)) return dnan_or(PINF);
282 return atan2(sqrt(u),x);
283}
284
285double WRAPPER_FUNC(atan)(double x) {
286 check_nan_d1(x);
287 if(dispinf(x)) return PI/2;
288 if(disminf(x)) return -PI/2;
289 return atan2(x,1);
290}
291
292double WRAPPER_FUNC(sinh)(double x) {
293 check_nan_d1(x);
294 return dldexp((exp(x)-exp(dneg(x))),-1);
295}
296
297double WRAPPER_FUNC(cosh)(double x) {
298 check_nan_d1(x);
299 return dldexp((exp(x)+exp(dneg(x))),-1);
300}
301
302double WRAPPER_FUNC(tanh)(double x) {
303 check_nan_d1(x);
304 double u;
305 int e;
306 e=dgetexp(x);
307 if(e>=5+0x3ff) { // |x|>=32?
308 if(!disneg(x)) return 1; // 1 << exp 2x; avoid generating infinities later
309 else return -1; // 1 >> exp 2x
310 }
311 u=exp(dldexp(x,1));
312 return (u-1)/(u+1);
313}
314
315double WRAPPER_FUNC(asinh)(double x) {
316 check_nan_d1(x);
317 int e;
318 e=dgetexp(x);
319 if(e>=32+0x3ff) { // |x|>=2^32?
320 if(!disneg(x)) return log( x )+LOG2; // 1/x^2 << 1
321 else return dneg(log(dneg(x))+LOG2); // 1/x^2 << 1
322 }
323 if(x>0) return log(sqrt(x*x+1)+x);
324 else return dneg(log(sqrt(x*x+1)-x));
325}
326
327double WRAPPER_FUNC(acosh)(double x) {
328 check_nan_d1(x);
329 int e;
330 if(disneg(x)) x=dneg(x);
331 e=dgetexp(x);
332 if(e>=32+0x3ff) return log(x)+LOG2; // |x|>=2^32?
333 return log(sqrt((x-1)*(x+1))+x);
334}
335
336double WRAPPER_FUNC(atanh)(double x) {
337 check_nan_d1(x);
338 return dldexp(log((1+x)/(1-x)),-1);
339}
340
341double WRAPPER_FUNC(exp2)(double x) {
342 check_nan_d1(x);
343 int e;
344 // extra check for disminf as this catches -Nan, and x<=-4096 doesn't.
345 if (disminf(x) || x<=-4096) return 0; // easily underflows
346 else if (x>=4096) return PINF; // easily overflows
347 e=(int)round(x);
348 x-=e;
349 return dldexp(exp(x*LOG2),e);
350}
351double WRAPPER_FUNC(log2)(double x) { check_nan_d1(x); return log(x)*LOG2E; }
352double WRAPPER_FUNC(exp10)(double x) { check_nan_d1(x); return pow(10,x); }
353double WRAPPER_FUNC(log10)(double x) { check_nan_d1(x); return log(x)*LOG10E; }
354
355// todo these are marked as lofi
356double WRAPPER_FUNC(expm1(double x) { check_nan_d1(x); return exp)(x)-1; }
357double WRAPPER_FUNC(log1p(double x) { check_nan_d1(x); return log)(1+x); }
358double WRAPPER_FUNC(fma)(double x,double y,double z) { check_nan_d1(x); return x*y+z; }
359
360// general power, x>0, finite
361static double dpow_1(double x,double y) {
362 int a,b,c;
363 double t,rt,u,v,v0,v1,w,ry;
364 a=dgetexp(x)-0x3ff;
365 u=log2(dldexp(x,-a)); // now log_2 x = a+u
366 if(u>0.5) u-=1,a++; // |u|<=~0.5
367 if(a==0) return exp2(u*y);
368 // here |log_2 x| >~0.5
369 if(y>= 4096) { // then easily over/underflows
370 if(a<0) return 0;
371 return PINF;
372 }
373 if(y<=-4096) { // then easily over/underflows
374 if(a<0) return PINF;
375 return 0;
376 }
377 ry=round(y);
378 v=y-ry;
379 v0=dldexp(round(ldexp(v,26)),-26);
380 v1=v-v0;
381 b=(int)ry; // guaranteed to fit in an int; y=b+v0+v1
382 // now the result is exp2( (a+u) * (b+v0+v1) )
383 c=a*b; // integer
384 t=a*v0;
385 rt=round(t);
386 c+=(int)rt;
387 w=t-rt;
388 t=a*v1;
389 w+=t;
390 t=u*b;
391 rt=round(t);
392 c+=(int)rt;
393 w+=t-rt;
394 w+=u*v;
395 return dldexp(exp2(w),c);
396}
397
398static double dpow_int2(double x,int y) {
399 double u;
400 if(y==1) return x;
401 u=dpow_int2(x,y/2);
402 u*=u;
403 if(y&1) u*=x;
404 return u;
405}
406
407// for the case where x not zero or infinity, y small and not zero
408static inline double dpowint_1(double x,int y) {
409 if(y<0) x=1/x,y=-y;
410 return dpow_int2(x,y);
411}
412
413// for the case where x not zero or infinity
414static double dpowint_0(double x,int y) {
415 int e;
416 if(disneg(x)) {
417 if(disoddint(y)) return dneg(dpowint_0(dneg(x),y));
418 else return dpowint_0(dneg(x),y);
419 }
420 if(dispo2(x)) {
421 e=dgetexp(x)-0x3ff;
422 if(y>=2048) y= 2047; // avoid overflow
423 if(y<-2048) y=-2048;
424 y*=e;
425 return dldexp(1,y);
426 }
427 if(y==0) return 1;
428 if(y>=-32&&y<=32) return dpowint_1(x,y);
429 return dpow_1(x,y);
430}
431
432double WRAPPER_FUNC(powint)(double x,int y) {
433 _Pragma("GCC diagnostic push")
434 _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"")
435 if(x==1.0||y==0) return 1;
436 _Pragma("GCC diagnostic pop")
437 check_nan_d1(x);
438 if(diszero(x)) {
439 if(y>0) {
440 if(y&1) return x;
441 else return 0;
442 }
443 if((y&1)) return dcopysign(PINF,x);
444 return PINF;
445 }
446 if(dispinf(x)) {
447 if(y<0) return 0;
448 else return PINF;
449 }
450 if(disminf(x)) {
451 if(y>0) {
452 if((y&1)) return MINF;
453 else return PINF;
454 }
455 if((y&1)) return MZERO;
456 else return PZERO;
457 }
458 return dpowint_0(x,y);
459}
460
461// for the case where y is guaranteed a finite integer, x not zero or infinity
462static double dpow_0(double x,double y) {
463 int e,p;
464 if(disneg(x)) {
465 if(disoddint(y)) return dneg(dpow_0(dneg(x),y));
466 else return dpow_0(dneg(x),y);
467 }
468 p=(int)y;
469 if(dispo2(x)) {
470 e=dgetexp(x)-0x3ff;
471 if(p>=2048) p= 2047; // avoid overflow
472 if(p<-2048) p=-2048;
473 p*=e;
474 return dldexp(1,p);
475 }
476 if(p==0) return 1;
477 if(p>=-32&&p<=32) return dpowint_1(x,p);
478 return dpow_1(x,y);
479}
480
481double WRAPPER_FUNC(pow)(double x,double y) {
482 _Pragma("GCC diagnostic push")
483 _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"")
484
485 if(x==1.0||diszero(y)) return 1;
486 check_nan_d2(x, y);
487 if(x==-1.0&&disinf(y)) return 1;
488 _Pragma("GCC diagnostic pop")
489
490 if(diszero(x)) {
491 if(!disneg(y)) {
492 if(disoddint(y)) return x;
493 else return 0;
494 }
495 if(disoddint(y)) return dcopysign(PINF,x);
496 return PINF;
497 }
498 if(dispinf(x)) {
499 if(disneg(y)) return 0;
500 else return PINF;
501 }
502 if(disminf(x)) {
503 if(!disneg(y)) {
504 if(disoddint(y)) return MINF;
505 else return PINF;
506 }
507 if(disoddint(y)) return MZERO;
508 else return PZERO;
509 }
510 if(dispinf(y)) {
511 if(dgetexp(x)<0x3ff) return PZERO;
512 else return PINF;
513 }
514 if(disminf(y)) {
515 if(dgetexp(x)<0x3ff) return PINF;
516 else return PZERO;
517 }
518 if(disint(y)) return dpow_0(x,y);
519 if(disneg(x)) return PINF;
520 return dpow_1(x,y);
521}
522
523double WRAPPER_FUNC(hypot)(double x,double y) {
524 check_nan_d2(x, y);
525 int ex,ey;
526 ex=dgetexp(x); ey=dgetexp(y);
527 if(ex>=0x3ff+400||ey>=0x3ff+400) { // overflow, or nearly so
528 x=dldexp(x,-600),y=dldexp(y,-600);
529 return dldexp(sqrt(x*x+y*y), 600);
530 }
531 else if(ex<=0x3ff-400&&ey<=0x3ff-400) { // underflow, or nearly so
532 x=dldexp(x, 600),y=dldexp(y, 600);
533 return dldexp(sqrt(x*x+y*y),-600);
534 }
535 return sqrt(x*x+y*y);
536}
537
538double WRAPPER_FUNC(cbrt)(double x) {
539 check_nan_d1(x);
540 int e;
541 if(disneg(x)) return dneg(cbrt(dneg(x)));
542 if(diszero(x)) return dcopysign(PZERO,x);
543 e=dgetexp(x)-0x3ff;
544 e=(e*0x5555+0x8000)>>16; // ~e/3, rounded
545 x=dldexp(x,-e*3);
546 x=exp(log(x)*ONETHIRD);
547 return dldexp(x,e);
548}
549
550// reduces mx*2^e modulo my, returning bottom bits of quotient at *pquo
551// 2^52<=|mx|,my<2^53, e>=0; 0<=result<my
552static i64 drem_0(i64 mx,i64 my,int e,int*pquo) {
553 int quo=0,q,r=0,s;
554 if(e>0) {
555 r=0xffffffffU/(ui32)(my>>36); // reciprocal estimate Q16
556 }
557 while(e>0) {
558 s=e; if(s>12) s=12; // gain up to 12 bits on each iteration
559 q=(mx>>38)*r; // Q30
560 q=((q>>(29-s))+1)>>1; // Q(s), rounded
561 mx=(mx<<s)-my*q;
562 quo=(quo<<s)+q;
563 e-=s;
564 }
565 if(mx>=my) mx-=my,quo++; // when e==0 mx can be nearly as big as 2my
566 if(mx>=my) mx-=my,quo++;
567 if(mx<0) mx+=my,quo--;
568 if(mx<0) mx+=my,quo--;
569 if(pquo) *pquo=quo;
570 return mx;
571}
572
573double WRAPPER_FUNC(fmod)(double x,double y) {
574 check_nan_d2(x, y);
575 ui64 ix=double2ui64(x),iy=double2ui64(y);
576 int sx,ex,ey;
577 i64 mx,my;
578 DUNPACKS(ix,sx,ex,mx);
579 DUNPACK(iy,ey,my);
580 if(ex==0x7ff) return dnan_or(PINF);
581 if(ey==0) return PINF;
582 if(ex==0) {
583 if(!disneg(x)) return PZERO;
584 return MZERO;
585 }
586 if(ex<ey) return x; // |x|<|y|, including case x=±0
587 mx=drem_0(mx,my,ex-ey,0);
588 if(sx) mx=-mx;
589 return fix642double(mx,0x3ff-ey+52);
590}
591
592double WRAPPER_FUNC(remquo)(double x,double y,int*quo) {
593 check_nan_d2(x, y);
594 ui64 ix=double2ui64(x),iy=double2ui64(y);
595 int sx,sy,ex,ey,q;
596 i64 mx,my;
597 DUNPACKS(ix,sx,ex,mx);
598 DUNPACKS(iy,sy,ey,my);
599 if(quo) *quo=0;
600 if(ex==0x7ff) return PINF;
601 if(ey==0) return PINF;
602 if(ex==0) return PZERO;
603 if(ey==0x7ff) return x;
604 if(ex<ey-1) return x; // |x|<|y|/2
605 if(ex==ey-1) {
606 if(mx<=my) return x; // |x|<=|y|/2, even quotient
607 // here |y|/2<|x|<|y|
608 if(!sx) { // x>|y|/2
609 mx-=my+my;
610 ey--;
611 q=1;
612 } else { // x<-|y|/2
613 mx=my+my-mx;
614 ey--;
615 q=-1;
616 }
617 }
618 else {
619 if(sx) mx=-mx;
620 mx=drem_0(mx,my,ex-ey,&q);
621 if(mx+mx>my || (mx+mx==my&&(q&1)) ) { // |x|>|y|/2, or equality and an odd quotient?
622 mx-=my;
623 q++;
624 }
625 }
626 if(sy) q=-q;
627 if(quo) *quo=q;
628 return fix642double(mx,0x3ff-ey+52);
629}
630
631double WRAPPER_FUNC(drem)(double x,double y) { check_nan_d2(x, y); return remquo(x,y,0); }
632
633double WRAPPER_FUNC(remainder)(double x,double y) { check_nan_d2(x, y); return remquo(x,y,0); }
634
635_Pragma("GCC diagnostic pop") // conversion