From 66c7b26f43ae057b88a9c94d11b5819dd94b0940 Mon Sep 17 00:00:00 2001 From: graehl Date: Sun, 15 Aug 2010 02:41:10 +0000 Subject: ftoa git-svn-id: https://ws10smt.googlecode.com/svn/trunk@549 ec762483-ff6d-05da-a07a-a48fb63a330f --- utils/ftoa.h | 402 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 402 insertions(+) create mode 100755 utils/ftoa.h diff --git a/utils/ftoa.h b/utils/ftoa.h new file mode 100755 index 00000000..81a685ac --- /dev/null +++ b/utils/ftoa.h @@ -0,0 +1,402 @@ +#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); +} + +// 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 -- cgit v1.2.3