/* * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ #include "pico/types.h" #include "pico/float.h" #include "pico/platform.h" // opened a separate issue https://github.com/raspberrypi/pico-sdk/issues/166 to deal with these warnings if at all _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wconversion\"") _Pragma("GCC diagnostic ignored \"-Wsign-conversion\"") typedef uint32_t ui32; typedef int32_t i32; #define FPINF ( HUGE_VALF) #define FMINF (-HUGE_VALF) #define NANF ((float)NAN) #define PZERO (+0.0) #define MZERO (-0.0) #define PI 3.14159265358979323846 #define LOG2 0.69314718055994530941 // Unfortunately in double precision ln(10) is very close to half-way between to representable numbers #define LOG10 2.30258509299404568401 #define LOG2E 1.44269504088896340737 #define LOG10E 0.43429448190325182765 #define ONETHIRD 0.33333333333333333333 #define PIf 3.14159265358979323846f #define LOG2f 0.69314718055994530941f #define LOG2Ef 1.44269504088896340737f #define LOG10Ef 0.43429448190325182765f #define ONETHIRDf 0.33333333333333333333f #define FUNPACK(x,e,m) e=((x)>>23)&0xff,m=((x)&0x007fffff)|0x00800000 #define FUNPACKS(x,s,e,m) s=((x)>>31),FUNPACK((x),(e),(m)) _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wstrict-aliasing\"") static inline bool fisnan(float x) { ui32 ix=*(ui32*)&x; return ix * 2 > 0xff000000u; } #if PICO_FLOAT_PROPAGATE_NANS #define check_nan_f1(x) if (fisnan((x))) return (x) #define check_nan_f2(x,y) if (fisnan((x))) return (x); else if (fisnan((y))) return (y); #else #define check_nan_f1(x) ((void)0) #define check_nan_f2(x,y) ((void)0) #endif static inline int fgetsignexp(float x) { ui32 ix=*(ui32*)&x; return (ix>>23)&0x1ff; } static inline int fgetexp(float x) { ui32 ix=*(ui32*)&x; return (ix>>23)&0xff; } static inline float fldexp(float x,int de) { ui32 ix=*(ui32*)&x,iy; int e; e=fgetexp(x); if(e==0||e==0xff) return x; e+=de; if(e<=0) iy=ix&0x80000000; // signed zero for underflow else if(e>=0xff) iy=(ix&0x80000000)|0x7f800000ULL; // signed infinity on overflow else iy=ix+((ui32)de<<23); return *(float*)&iy; } float WRAPPER_FUNC(ldexpf)(float x, int de) { check_nan_f1(x); return fldexp(x, de); } static inline float fcopysign(float x,float y) { ui32 ix=*(ui32*)&x,iy=*(ui32*)&y; ix=((ix&0x7fffffff)|(iy&0x80000000)); return *(float*)&ix; } float WRAPPER_FUNC(copysignf)(float x, float y) { check_nan_f2(x,y); return fcopysign(x, y); } static inline int fiszero(float x) { return fgetexp (x)==0; } static inline int fispzero(float x) { return fgetsignexp(x)==0; } static inline int fismzero(float x) { return fgetsignexp(x)==0x100; } static inline int fisinf(float x) { return fgetexp (x)==0xff; } static inline int fispinf(float x) { return fgetsignexp(x)==0xff; } static inline int fisminf(float x) { return fgetsignexp(x)==0x1ff; } static inline int fisint(float x) { ui32 ix=*(ui32*)&x,m; int e=fgetexp(x); if(e==0) return 1; // 0 is an integer e-=0x7f; // remove exponent bias if(e<0) return 0; // |x|<1 e=23-e; // bit position in mantissa with significance 1 if(e<=0) return 1; // |x| large, so must be an integer m=(1<>e)&1; } static inline int fisstrictneg(float x) { ui32 ix=*(ui32*)&x; if(fiszero(x)) return 0; return ix>>31; } static inline int fisneg(float x) { ui32 ix=*(ui32*)&x; return ix>>31; } static inline float fneg(float x) { ui32 ix=*(ui32*)&x; ix^=0x80000000; return *(float*)&ix; } static inline int fispo2(float x) { ui32 ix=*(ui32*)&x; if(fiszero(x)) return 0; if(fisinf(x)) return 0; ix&=0x007fffff; return ix==0; } static inline float fnan_or(float x) { #if PICO_FLOAT_PROPAGATE_NANS return NANF; #else return x; #endif } float WRAPPER_FUNC(truncf)(float x) { check_nan_f1(x); ui32 ix=*(ui32*)&x,m; int e=fgetexp(x); e-=0x7f; // remove exponent bias if(e<0) { // |x|<1 ix&=0x80000000; return *(float*)&ix; } e=23-e; // bit position in mantissa with significance 1 if(e<=0) return x; // |x| large, so must be an integer m=(1<=4+0x7f) { // |x|>=16? if(!fisneg(x)) return 1; // 1 << exp 2x; avoid generating infinities later else return -1; // 1 >> exp 2x } u=expf(fldexp(x,1)); return (u-1.0f)/(u+1.0f); } float WRAPPER_FUNC(asinhf)(float x) { check_nan_f1(x); int e; e=fgetexp(x); if(e>=16+0x7f) { // |x|>=2^16? if(!fisneg(x)) return logf( x )+LOG2f; // 1/x^2 << 1 else return fneg(logf(fneg(x))+LOG2f); // 1/x^2 << 1 } if(x>0) return (float)log(sqrt((double)x*(double)x+1.0)+(double)x); else return fneg((float)log(sqrt((double)x*(double)x+1.0)-(double)x)); } float WRAPPER_FUNC(acoshf)(float x) { check_nan_f1(x); int e; if(fisneg(x)) x=fneg(x); e=fgetexp(x); if(e>=16+0x7f) return logf(x)+LOG2f; // |x|>=2^16? return (float)log(sqrt(((double)x+1.0)*((double)x-1.0))+(double)x); } float WRAPPER_FUNC(atanhf)(float x) { check_nan_f1(x); return fldexp(logf((1.0f+x)/(1.0f-x)),-1); } float WRAPPER_FUNC(exp2f)(float x) { check_nan_f1(x); return (float)exp((double)x*LOG2); } float WRAPPER_FUNC(log2f)(float x) { check_nan_f1(x); return logf(x)*LOG2Ef; } float WRAPPER_FUNC(exp10f)(float x) { check_nan_f1(x); return (float)exp((double)x*LOG10); } float WRAPPER_FUNC(log10f)(float x) { check_nan_f1(x); return logf(x)*LOG10Ef; } float WRAPPER_FUNC(expm1f)(float x) { check_nan_f1(x); return (float)(exp((double)x)-1); } float WRAPPER_FUNC(log1pf)(float x) { check_nan_f1(x); return (float)(log(1+(double)x)); } float WRAPPER_FUNC(fmaf)(float x,float y,float z) { check_nan_f2(x,y); check_nan_f1(z); return (float)((double)x*(double)y+(double)z); } // has double rounding so not exact // general power, x>0 static inline float fpow_1(float x,float y) { return (float)exp(log((double)x)*(double)y); // using double-precision intermediates for better accuracy } static float fpow_int2(float x,int y) { float u; if(y==1) return x; u=fpow_int2(x,y/2); u*=u; if(y&1) u*=x; return u; } // for the case where x not zero or infinity, y small and not zero static inline float fpowint_1(float x,int y) { if(y<0) x=1.0f/x,y=-y; return fpow_int2(x,y); } // for the case where x not zero or infinity static float fpowint_0(float x,int y) { int e; if(fisneg(x)) { if(fisoddint(y)) return fneg(fpowint_0(fneg(x),y)); else return fpowint_0(fneg(x),y); } if(fispo2(x)) { e=fgetexp(x)-0x7f; if(y>=256) y= 255; // avoid overflow if(y<-256) y=-256; y*=e; return fldexp(1,y); } if(y==0) return 1; if(y>=-32&&y<=32) return fpowint_1(x,y); return fpow_1(x,y); } float WRAPPER_FUNC(powintf)(float x,int y) { _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"") if(x==1.0f||y==0) return 1; if(x==0.0f) { if(y>0) { if(y&1) return x; else return 0; } if((y&1)) return fcopysign(FPINF,x); return FPINF; } _Pragma("GCC diagnostic pop") check_nan_f1(x); if(fispinf(x)) { if(y<0) return 0; else return FPINF; } if(fisminf(x)) { if(y>0) { if((y&1)) return FMINF; else return FPINF; } if((y&1)) return MZERO; else return PZERO; } return fpowint_0(x,y); } // for the case where y is guaranteed a finite integer, x not zero or infinity static float fpow_0(float x,float y) { int e,p; if(fisneg(x)) { if(fisoddint(y)) return fneg(fpow_0(fneg(x),y)); else return fpow_0(fneg(x),y); } p=(int)y; if(fispo2(x)) { e=fgetexp(x)-0x7f; if(p>=256) p= 255; // avoid overflow if(p<-256) p=-256; p*=e; return fldexp(1,p); } if(p==0) return 1; if(p>=-32&&p<=32) return fpowint_1(x,p); return fpow_1(x,y); } float WRAPPER_FUNC(powf)(float x,float y) { _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"") if(x==1.0f||fiszero(y)) return 1; check_nan_f2(x,y); if(x==-1.0f&&fisinf(y)) return 1; _Pragma("GCC diagnostic pop") if(fiszero(x)) { if(!fisneg(y)) { if(fisoddint(y)) return x; else return 0; } if(fisoddint(y)) return fcopysign(FPINF,x); return FPINF; } if(fispinf(x)) { if(fisneg(y)) return 0; else return FPINF; } if(fisminf(x)) { if(!fisneg(y)) { if(fisoddint(y)) return FMINF; else return FPINF; } if(fisoddint(y)) return MZERO; else return PZERO; } if(fispinf(y)) { if(fgetexp(x)<0x7f) return PZERO; else return FPINF; } if(fisminf(y)) { if(fgetexp(x)<0x7f) return FPINF; else return PZERO; } if(fisint(y)) return fpow_0(x,y); if(fisneg(x)) return FPINF; return fpow_1(x,y); } float WRAPPER_FUNC(hypotf)(float x,float y) { check_nan_f2(x,y); int ex,ey; ex=fgetexp(x); ey=fgetexp(y); if(ex>=0x7f+50||ey>=0x7f+50) { // overflow, or nearly so x=fldexp(x,-70),y=fldexp(y,-70); return fldexp(sqrtf(x*x+y*y), 70); } else if(ex<=0x7f-50&&ey<=0x7f-50) { // underflow, or nearly so x=fldexp(x, 70),y=fldexp(y, 70); return fldexp(sqrtf(x*x+y*y),-70); } return sqrtf(x*x+y*y); } float WRAPPER_FUNC(cbrtf)(float x) { check_nan_f1(x); int e; if(fisneg(x)) return fneg(cbrtf(fneg(x))); if(fiszero(x)) return fcopysign(PZERO,x); e=fgetexp(x)-0x7f; e=(e*0x5555+0x8000)>>16; // ~e/3, rounded x=fldexp(x,-e*3); x=expf(logf(x)*ONETHIRDf); return fldexp(x,e); } // reduces mx*2^e modulo my, returning bottom bits of quotient at *pquo // 2^23<=|mx|,my<2^24, e>=0; 0<=result0) { r=0xffffffffU/(ui32)(my>>7); // reciprocal estimate Q16 } while(e>0) { s=e; if(s>12) s=12; // gain up to 12 bits on each iteration q=(mx>>9)*r; // Q30 q=((q>>(29-s))+1)>>1; // Q(s), rounded mx=(mx<=my) mx-=my,quo++; // when e==0 mx can be nearly as big as 2my if(mx>=my) mx-=my,quo++; if(mx<0) mx+=my,quo--; if(mx<0) mx+=my,quo--; if(pquo) *pquo=quo; return mx; } float WRAPPER_FUNC(fmodf)(float x,float y) { check_nan_f2(x,y); ui32 ix=*(ui32*)&x,iy=*(ui32*)&y; int sx,ex,ey; i32 mx,my; FUNPACKS(ix,sx,ex,mx); FUNPACK(iy,ey,my); if(ex==0xff) { return fnan_or(FPINF); } if(ey==0) return FPINF; if(ex==0) { if(!fisneg(x)) return PZERO; return MZERO; } if(ex|y|/2 mx-=my+my; ey--; q=1; } else { // x<-|y|/2 mx=my+my-mx; ey--; q=-1; } } else { if(sx) mx=-mx; mx=frem_0(mx,my,ex-ey,&q); if(mx+mx>my || (mx+mx==my&&(q&1)) ) { // |x|>|y|/2, or equality and an odd quotient? mx-=my; q++; } } if(sy) q=-q; if(quo) *quo=q; return fix2float(mx,0x7f-ey+23); } float WRAPPER_FUNC(dremf)(float x,float y) { check_nan_f2(x,y); return remquof(x,y,0); } float WRAPPER_FUNC(remainderf)(float x,float y) { check_nan_f2(x,y); return remquof(x,y,0); } _Pragma("GCC diagnostic pop") // strict-aliasing _Pragma("GCC diagnostic pop") // conversion