aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <>2023-07-03 00:40:37 +0000
committerdrh <>2023-07-03 00:40:37 +0000
commitc27bda0b7911186d1e11c8ffcd55818cfa211009 (patch)
tree82dcb51d1cb927c397471874a9cece6b03f4ddab /src
parentfecdff43545a50c14634ed087682635d1405f440 (diff)
downloadsqlite-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.c64
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;