#ifndef FTOA_H #define FTOA_H //TODO: for fractional digits/non-sci, determine the right amount of left padding (more if the whole number is indeed <1, to keep the significant digits), less if sci notation and/or mantissa has sig. digits (don't want N before . and N after!) #ifndef FTOA_ROUNDTRIP # define FTOA_ROUNDTRIP 1 #endif #ifndef FTOA_DEBUG # define FTOA_DEBUG 0 #endif #ifndef FTOA_USE_SPRINTF #define FTOA_USE_SPRINTF 0 #endif #if FTOA_DEBUG # define FTOAassert(x) assert(x) # define DBFTOA(x) std::cerr<<"\nFTOA " <<__func__<<"("<<__LINE__<<"): " #x "="< #include #include #include #include #include #include "utoa.h" #include "nan.h" template struct ftoa_traits { }; //eP10, // sigd decimal places normally printed, roundtripd needed so that round-trip float->string->float is identity #define DEFINE_FTOA_TRAITS(FLOATT,INTT,sigd,roundtripd,small,large,used,P10) \ template <> \ struct ftoa_traits { \ typedef INTT int_t; \ typedef u ## INTT uint_t; \ typedef FLOATT float_t; \ enum { digits10=std::numeric_limits::digits10, chars_block=P10, usedig=used, sigdig=sigd, roundtripdig=roundtripd, bufsize=roundtripdig+7 }; \ static const double pow10_block = 1e ## P10; \ static const float_t small_f = small; \ static const float_t large_f = large; \ static inline int sprintf(char *buf,double f) { return std::sprintf(buf,"%." #used "g",f); } \ static inline int sprintf_sci(char *buf,double f) { return std::sprintf(buf,"%." #used "e",f); } \ static inline int sprintf_nonsci(char *buf,double f) { return std::sprintf(buf,"%." #used "f",f); } \ static inline uint_t fracblock(double frac) { FTOAassert(frac>=0 && frac<1); double f=frac*pow10_block;uint_t i=(uint_t)f;FTOAassert(i=0 && frac<1); double f=frac*pow10_block;uint_t i=(uint_t)(f+.5);FTOAassert(ilarge; } \ static inline bool use_sci(float_t f) { return use_sci_abs(std::fabs(f)); } \ }; //TODO: decide on computations in double (would hurt long double) or in native float type - any advantage? more precision is usually better. //10^22 = 0x1.0f0cf064dd592p73 is the largest exactly representable power of 10 in the binary64 format. but round down to 18 so int64_t can hold it. #if FTOA_ROUNDTRIP #define DEFINE_FTOA_TRAITS_ROUNDTRIP(FLOATT,INTT,sigd,roundtripd,small,large) DEFINE_FTOA_TRAITS(FLOATT,INTT,sigd,roundtripd,small,large,roundtripd,roundtripd) #else #define DEFINE_FTOA_TRAITS_ROUNDTRIP(FLOATT,INTT,sigd,roundtripd,small,large) DEFINE_FTOA_TRAITS(FLOATT,INTT,sigd,roundtripd,small,large,sigd,sigd) #endif DEFINE_FTOA_TRAITS_ROUNDTRIP(double,int64_t,15,17,1e-5,1e8) //i've heard that 1e10 is fine for float. but we only have 1e9 (9 decimal places) in int32. DEFINE_FTOA_TRAITS_ROUNDTRIP(float,int32_t,6,9,1e-3,1e8) template inline void ftoa_error(F f,char const* msg="") { using namespace std; cerr<<"ftoa error: "< char *prepend_pos_frac_digits(char *p,F f) { FTOAassert(f<1 && f >0); typedef ftoa_traits FT; //repeat if very small??? nah, require sci notation to take care of it. typename FT::uint_t i=FT::rounded_fracblock(f); DBFTOA2(f,i); if (i>0) { unsigned n_skipped; char *d=utoa_drop_trailing_0(p,i,n_skipped); char *b=p-FT::chars_block+n_skipped; FTOAassert(b<=d); left_pad(b,d,'0'); return b; } else { return p; } } template char *append_pos_frac_digits(char *p,F f) { // '0' right-padded, nul terminated, return position of nul. [p,ret) are the digits if (f==0) { *p++='0'; return p; } FTOAassert(f<1 && f >0); typedef ftoa_traits FT; //repeat if very small??? nah, require sci notation to take care of it. typename FT::uint_t i=FT::rounded_fracblock(f); DBFTOA2(f,i); if (i>0) { char *e=p+FT::chars_block; utoa_left_pad(p,e,i,'0'); *e=0; return e; } else { *p=0; return p; } } template inline char *prepend_pos_frac(char *p,F f) { FTOAassert(f<1 && f>=0); if (f==0) { *--p='0'; return p; } p=prepend_pos_frac_digits(p,f); *--p='.'; if (DECIMAL_FOR_WHOLE>0) *--p='0'; return p; } template inline char *append_pos_frac(char *p,F f) { DBFTOA(f); if (DECIMAL_FOR_WHOLE>0) *p++='0'; *p++='.'; return append_pos_frac_digits(p,f); } template inline char *prepend_frac(char *p,F f,bool positive_sign=false) { FTOAassert(f<1 && f>-1); if (f==0) *--p='0'; else if (f<0) { p=prepend_pos_frac(p,-f); *--p='-'; } else { p=prepend_pos_frac(p,f); if (positive_sign) *--p='+'; } return p; } template inline char *append_sign(char *p,F f,bool positive_sign=false) { if (f<0) { *p++='-'; } else if (positive_sign) *p++='+'; return p; } template inline char *append_frac(char *p,F f,bool positive_sign=false) { FTOAassert(f<1 && f>-1); if (f==0) { *p++='0'; return p; } else if (f<0) { *p++='-'; return append_pos_frac(p,-f); } if (positive_sign) { *p++='+'; return append_pos_frac(p,f); } } //append_frac, append_pos_sci, append_sci. notice these are all composed according to a pattern (but reversing order of composition in pre vs app). or can implement with copy through buffer /* will switch to sci notation if integer part is too big for the int type. but for very small values, will simply display 0 (i.e. //TODO: find out log10 and leftpad 0s then convert rest) */ template char *prepend_pos_nonsci(char *p,F f) { typedef ftoa_traits FT; typedef typename FT::uint_t uint_t; DBFTOA(f); FTOAassert(f>0); if (f>std::numeric_limits::max()) return prepend_pos_sci(p,f); //which is faster - modf is weird and returns negative frac part if f is negative. while we could deal with this using fabs, we instead only handle positive here (put - sign in front and negate, then call us) - ? #if 0 F intpart; F frac=std::modf(f,&intpart); uint_t u=intpart; #else uint_t u=f; F frac=f-u; #endif DBFTOA2(u,frac); if (frac == 0) { if (DECIMAL_FOR_WHOLE>1) *--p='.'; } else { p=prepend_pos_frac_digits(p,frac); *--p='.'; } if (u==0) { if (DECIMAL_FOR_WHOLE>0) *--p='0'; } else p=utoa(p,u); return p; } // modify p; return true if handled template inline bool prepend_0_etc(char *&p,F f,bool positive_sign=false) { if (f==0) { *--p='0'; return true; } if (is_nan(f)) { p-=3; p[0]='N';p[1]='A';p[2]='N'; return true; } if (is_pos_inf(f)) { p-=3; p[0]='I';p[1]='N';p[2]='F'; if (positive_sign) *--p='+'; return true; } if (is_neg_inf(f)) { p-=4; p[0]='-';p[1]='I';p[2]='N';p[3]='F'; return true; } return false; } template inline char *prepend_nonsci(char *p,F f,bool positive_sign=false) { if (prepend_0_etc(p,f,positive_sign)) return p; if (f<0) { p=prepend_pos_nonsci(p,-f); *--p='-'; } else { p=prepend_pos_nonsci(p,f); if (positive_sign) *--p='+'; } return p; } template inline char *prepend_pos_sci(char *p,F f,bool positive_sign_exp=false) { FTOAassert(f>0); typedef ftoa_traits FT; int e10; F mant=FT::mantexp10(f,e10); DBFTOA(f); DBFTOA2(mant,e10); FTOAassert(mant<10.00001); if (mant>=10.) { ++e10; mant*=.1; } else if (mant < 1.) { --e10; mant*=10; } p=itoa(p,e10,positive_sign_exp); *--p='e'; return prepend_pos_nonsci(p,mant); } template inline char *prepend_sci(char *p,F f,bool positive_sign_mant=false,bool positive_sign_exp=false) { if (prepend_0_etc(p,f,positive_sign_mant)) return p; if (f==0) *--p='0'; else if (f<0) { p=prepend_pos_sci(p,-f,positive_sign_exp); *--p='-'; } else { p=prepend_pos_sci(p,f,positive_sign_exp); if (positive_sign_mant) *--p='+'; } return p; } template inline char *append_nonsci(char *p,F f,bool positive_sign=false) { if (positive_sign&&f>=0) *p++='+'; return p+ftoa_traits::sprintf_nonsci(p,f); } template inline char *append_sci(char *p,F f,bool positive_sign=false) { if (positive_sign&&f>=0) *p++='+'; return p+ftoa_traits::sprintf_sci(p,f); } template inline char *append_ftoa(char *p,F f,bool positive_sign=false) { if (positive_sign&&f>=0) *p++='+'; return p+ftoa_traits::sprintf(p,f); } template inline char *prepend_ftoa(char *p,F f) { typedef ftoa_traits FT; return FT::use_sci(f) ? prepend_sci(p,f) : prepend_nonsci(p,f); } template inline std::string ftos_append(F f) { typedef ftoa_traits FT; char buf[FT::bufsize]; return std::string(buf,append_ftoa(buf,f)); } template inline std::string ftos_prepend(F f) { typedef ftoa_traits FT; char buf[FT::bufsize]; char *end=buf+FT::bufsize; return std::string(prepend_ftoa(end,f),end); } template inline std::string ftos(F f) { #if 0 // trust RVO? no extra copies? return FTOA_USE_SPRINTF ? ftos_append(f) : ftos_prepend(f); #else typedef ftoa_traits FT; char buf[FT::bufsize]; if (FTOA_USE_SPRINTF) { return std::string(buf,append_ftoa(buf,f)); } else { char *end=buf+FT::bufsize; return std::string(prepend_ftoa(end,f),end); } #endif } namespace { const int ftoa_bufsize=30; char ftoa_outbuf[ftoa_bufsize]; } // not even THREADLOCAL - don't use. inline char *static_ftoa(float f) { if (FTOA_USE_SPRINTF) { append_ftoa(ftoa_outbuf,f); return ftoa_outbuf; } else { char *end=ftoa_outbuf+ftoa_bufsize; return prepend_ftoa(end,f); } } #endif