diff options
author | drh <> | 2023-07-03 00:40:37 +0000 |
---|---|---|
committer | drh <> | 2023-07-03 00:40:37 +0000 |
commit | c27bda0b7911186d1e11c8ffcd55818cfa211009 (patch) | |
tree | 82dcb51d1cb927c397471874a9cece6b03f4ddab /src | |
parent | fecdff43545a50c14634ed087682635d1405f440 (diff) | |
download | sqlite-c27bda0b7911186d1e11c8ffcd55818cfa211009.tar.gz sqlite-c27bda0b7911186d1e11c8ffcd55818cfa211009.zip |
An idea for improving accuracy of fp-to-decimal conversion for systems that
do not have "long double". Does not quite work. Retained only for
documentation purposes.
FossilOrigin-Name: 702243333843219f8904ee0fd12283080d6c6b3fc1ffb36c534cdefb3563c00d
Diffstat (limited to 'src')
-rw-r--r-- | src/util.c | 64 |
1 files changed, 45 insertions, 19 deletions
diff --git a/src/util.c b/src/util.c index a560f2613..9bc37c4f9 100644 --- a/src/util.c +++ b/src/util.c @@ -937,16 +937,18 @@ int sqlite3Atoi(const char *z){ ** decimal point if n is negative. No rounding is performed if ** n is zero. */ -void sqlite3FpDecode(FpDecode *p, double rr, int iRound, int mxRound){ +void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ int i; u64 v; int e, exp = 0; - long double r; p->isSpecial = 0; - if( rr<0.0 ){ + + /* Convert negative numbers to positive. Deal with Infinity, 0.0, and + ** NaN. */ + if( r<0.0 ){ p->sign = '-'; - rr = -rr; - }else if( rr==0.0 ){ + r = -r; + }else if( r==0.0 ){ p->sign = '+'; p->n = 1; p->iDP = 1; @@ -955,7 +957,7 @@ void sqlite3FpDecode(FpDecode *p, double rr, int iRound, int mxRound){ }else{ p->sign = '+'; } - memcpy(&v,&rr,8); + memcpy(&v,&r,8); e = v>>52; if( (e&0x7ff)==0x7ff ){ p->isSpecial = 1 + (v!=0x7ff0000000000000L); @@ -963,23 +965,47 @@ void sqlite3FpDecode(FpDecode *p, double rr, int iRound, int mxRound){ p->iDP = 0; return; } - r = rr; - /* At this point, r is positive (non-zero) and is not Inf or NaN. - ** The strategy is to multiple or divide r by powers of 10 until - ** it is in between 1.0e+17 and 1.0e+19. Then convert r into - ** an unsigned 64-bit integer v, and extract digits from v. + /* Multiply r by powers of ten until it lands somewhere in between + ** 1.0e+19 and 1.0e+17. */ - if( r>=1.0e+19 ){ - while( r>=1.0e+119L ){ exp+=100; r *= 1.0e-100L; } - while( r>=1.0e+29L ){ exp+=10; r *= 1.0e-10L; } - while( r>=1.0e+19L ){ exp++; r *= 1.0e-1L; } + if( sizeof(long double)>8 + || (v & 0x0000000000ffffffLL)==0 + || (v & 0xffffffffff000000LL)==0 + ){ + long double rr = r; + if( rr>=1.0e+19 ){ + while( rr>=1.0e+119L ){ exp+=100; rr *= 1.0e-100L; } + while( rr>=1.0e+29L ){ exp+=10; rr *= 1.0e-10L; } + while( rr>=1.0e+19L ){ exp++; rr *= 1.0e-1L; } + }else{ + while( rr<1.0e-97L ){ exp-=100; rr *= 1.0e+100L; } + while( rr<1.0e+07L ){ exp-=10; rr *= 1.0e+10L; } + while( rr<1.0e+17L ){ exp--; rr *= 1.0e+1L; } + } + v = (u64)rr; }else{ - while( r<1.0e-97L ){ exp-=100; r *= 1.0e+100L; } - while( r<1.0e+07L ){ exp-=10; r *= 1.0e+10L; } - while( r<1.0e+17L ){ exp--; r *= 1.0e+1L; } + double r2, r3; + v &= 0xffffffffff000000LL; + memcpy(&r2, &v, 8); + assert( r2<r ); + assert( r2>0.0 ); + r3 = r - r2; + assert( r3>0.0 ); + if( r2>=1.0e+19 ){ + while( r2>=1.0e+119L ){ exp+=100; r2 *= 1.0e-100; r3 *= 1.0e-100; } + while( r2>=1.0e+29L ){ exp+=10; r2 *= 1.0e-10; r3 *= 1.0e-10; } + while( r2>=1.0e+19L ){ exp++; r2 *= 1.0e-1; r3 *= 1.0e-1; } + }else{ + while( r2<1.0e-97L ){ exp-=100; r2 *= 1.0e+100; r3 *= 1.0e+100; } + while( r2<1.0e+07L ){ exp-=10; r2 *= 1.0e+10; r3 *= 1.0e+10; } + while( r2<1.0e+17L ){ exp--; r2 *= 1.0e+1; r3 *= 1.0e+1; } + } + v = (u64)(r2+r3); } - v = (u64)r; + + + /* Extract significant digits. */ i = sizeof(p->z)-1; while( v ){ p->z[i--] = (v%10) + '0'; v /= 10; } p->n = sizeof(p->z) - 1 - i; |