From a1b0ff173520fc73a182e57b06b1d0228b343711 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 30 Jun 2023 18:35:43 +0000 Subject: [PATCH 01/17] Experiments with a new algorithm for converting ieee-754 binary64 numbers into decimal. FossilOrigin-Name: e923405e448385085224f9289991b303d86b02763535ea77d6fcee98ba6fc1f2 --- manifest | 22 +++++++++-------- manifest.uuid | 2 +- src/func.c | 22 +++++++++++++++++ src/sqliteInt.h | 15 ++++++++++++ src/util.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 113 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 0b84625ebf..95d4799b5d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\ssupport\sfor\ssqlite3_stmt_scanstatus_v2()\sprofiling\sof\sGROUP\sBY\sclauses\sthat\suse\sa\stemp\sb-tree,\sand\sfor\ssub-queries\simplemented\sas\sco-routines. -D 2023-06-30T18:31:37.545 +C Experiments\swith\sa\snew\salgorithm\sfor\sconverting\sieee-754\sbinary64\snumbers\ninto\sdecimal. +D 2023-06-30T18:35:43.344 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -590,7 +590,7 @@ F src/delete.c cd5f5cd06ed0b6a882ec1a8c2a0d73b3cecb28479ad19e9931c4706c5e2182be F src/expr.c 8d1656b65e26af3e34f78e947ac423f0d20c214ed25a67486e433bf16ca6b543 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c a7fcbf7e66d14dbb73cf49f31489ebf66d0e6006c62b95246924a3bae9f37b36 -F src/func.c 6b4804738b4d869f40625958b476a8f964d3df65b626e72a530d76051863cf32 +F src/func.c 699e19ca0dc580a024eb343f3a3abfb646e5438e36d377023d485a3038d789d5 F src/global.c bd0892ade7289f6e20bff44c07d06371f2ff9b53cea359e7854b9b72f65adc30 F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220 F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 @@ -642,7 +642,7 @@ F src/shell.c.in 2c02c819349de410d63fcc0217763dfe5a42dbe58f2d68046d4ea8a376d12c2 F src/sqlite.h.in 3076d78836b6dac53b3ab0875fc8fd15bca8077aad4d33c85336e05af6aef8c7 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4 -F src/sqliteInt.h b677ba33397479f9a2fa1223fcd24b561cc8c77c7d073798f7b7118e15d69dbb +F src/sqliteInt.h 6ef127217c54e4707b1ca500508340542ca3c2a5aa08ee7a00aaa4834e68992d F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -705,7 +705,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c 4264102045fdb36e9af3ff361e390a5f7a76342a2bd7069e55d8ad332026d6b5 +F src/util.c 447fd1d981bab37788397c24f980ce555438168f28badde1227441f6d67f1214 F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 @@ -2041,9 +2041,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f936f101406069b29218c89a36581b4497226fb61906782ea368f12d943c901c 4e8718dc35dbbaf75f17265a88d14acd9750dc75efbadf41377f9c97e732009c -R 4da01a7efae23c104b98c2eba70ec2ef -T +closed 4e8718dc35dbbaf75f17265a88d14acd9750dc75efbadf41377f9c97e732009c -U dan -Z 8ca5997f8884b68f398b8d0f077d59ae +P 7afad1f759f7ceda873c6d869422fd56fe4399c2d24d47ad9bc3b84b06d830d1 +R b84c58ae5f50c9a21c1125fd73d7f10d +T *branch * fp-to-decimal-refactor +T *sym-fp-to-decimal-refactor * +T -sym-trunk * +U drh +Z aad0befdc05a87e7f362b0543d2a6962 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f482cd32b9..2a088950c5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7afad1f759f7ceda873c6d869422fd56fe4399c2d24d47ad9bc3b84b06d830d1 \ No newline at end of file +e923405e448385085224f9289991b303d86b02763535ea77d6fcee98ba6fc1f2 \ No newline at end of file diff --git a/src/func.c b/src/func.c index b243591863..4cf930b208 100644 --- a/src/func.c +++ b/src/func.c @@ -2371,6 +2371,23 @@ static void signFunc( sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0); } + +#if 1 /* Temporary prototyping logic */ +static void fpdecodeFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + double r = sqlite3_value_double(argv[0]); + FpDecode s; + char zBuf[50]; + UNUSED_PARAMETER(argc); + sqlite3FpDecode(&s, r); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%c%.*s/%d", s.sign, s.n, s.z, s.iDP); + sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); +} +#endif /* Temporary prototyping logic */ + /* ** All of the FuncDef structures in the aBuiltinFunc[] array above ** to the global function hash table. This occurs at start-time (as @@ -2442,6 +2459,11 @@ void sqlite3RegisterBuiltinFunctions(void){ FUNCTION(unicode, 1, 0, 0, unicodeFunc ), FUNCTION(char, -1, 0, 0, charFunc ), FUNCTION(abs, 1, 0, 0, absFunc ), + +#if 1 /* Temporary prototyping function */ + FUNCTION(fpdecode, 1, 0, 0, fpdecodeFunc ), +#endif + #ifndef SQLITE_OMIT_FLOATING_POINT FUNCTION(round, 1, 0, 0, roundFunc ), FUNCTION(round, 2, 0, 0, roundFunc ), diff --git a/src/sqliteInt.h b/src/sqliteInt.h index d7a2d90fc2..b4705b5e26 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1239,6 +1239,7 @@ typedef struct Schema Schema; typedef struct Expr Expr; typedef struct ExprList ExprList; typedef struct FKey FKey; +typedef struct FpDecode FpDecode; typedef struct FuncDestructor FuncDestructor; typedef struct FuncDef FuncDef; typedef struct FuncDefHash FuncDefHash; @@ -4597,6 +4598,20 @@ struct PrintfArguments { sqlite3_value **apArg; /* The argument values */ }; +/* +** An instance of this object receives the decoding of a floating point +** value into an approximate decimal representation. +*/ +struct FpDecode { + char sign; /* '+' or '-' */ + char isNan; /* True if not-a-number */ + char isInf; /* True if infinity */ + int n; /* Significant digits in the decode */ + int iDP; /* Location of the decimal point */ + char z[24]; /* Significiant digits */ +}; + +void sqlite3FpDecode(FpDecode*,double); char *sqlite3MPrintf(sqlite3*,const char*, ...); char *sqlite3VMPrintf(sqlite3*,const char*, va_list); #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) diff --git a/src/util.c b/src/util.c index abd36eda87..29bc8524da 100644 --- a/src/util.c +++ b/src/util.c @@ -928,6 +928,69 @@ int sqlite3Atoi(const char *z){ return x; } +/* +** Decode a floating-point value into an approximate decimal +** representation. +*/ +void sqlite3FpDecode(FpDecode *p, double r){ + int i; + u64 v; + int e, exp = 0; + if( r<0.0 ){ + p->sign = '-'; + r = -r; + }else if( r==0.0 ){ + p->sign = '+'; + p->n = 1; + p->iDP = 1; + p->z[0] = '0'; + return; + }else{ + p->sign = '+'; + } + memcpy(&v,&r,8); + e = v>>52; + if( e==0x7ff ){ + if( v==0x7ff0000000000000L ){ + p->isInf = 1; + p->isNan = 0; + p->z[0] = 'I'; + p->z[1] = 'n'; + p->z[2] = 'f'; + }else{ + p->isInf = 0; + p->isNan = 1; + p->z[0] = 'N'; + p->z[1] = 'a'; + p->z[2] = 'N'; + } + p->n = 3; + p->iDP = 3; + return; + } + /* 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. + */ + if( r>=1.0e+19 ){ + while( r>=1.0e+119 ){ exp+=100; r /= 1.0e+100; } + while( r>=1.0e+29 ){ exp+=10; r /= 1.0e+10; } + while( r>=1.0e+19 ){ exp++; r /= 10.0; } + }else if( r<1.0e+17 ){ + while( r<1.0e-97 ){ exp-=100; r *= 1.0e+100; } + while( r<1.0e+07 ){ exp-=10; r *= 1.0e+10; } + while( r<1.0e+17 ){ exp--; r *= 10.0; } + } + v = (u64)r; + i = sizeof(p->z); + while( v ){ p->z[i--] = (v%10) + '0'; v /= 10; } + p->n = sizeof(p->z) - i; + p->iDP = p->n + exp; + memmove(p->z, &p->z[i+1], p->n); + while( p->n>0 && p->z[p->n-1]=='0' ){ p->n--; } +} + /* ** Try to convert z into an unsigned 32-bit integer. Return true on ** success and false if there is an error. From 002330dc483ff706bcce8f0df2851dd6c656fc55 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 30 Jun 2023 19:41:57 +0000 Subject: [PATCH 02/17] Add decimal rounding to the sqlite3FpDecode() routine. FossilOrigin-Name: 27871140caa833f0bc0962e44356993938e93dcf81c1074382b1560a3e1aeb61 --- manifest | 19 ++++++++----------- manifest.uuid | 2 +- src/func.c | 7 +++++-- src/sqliteInt.h | 2 +- src/util.c | 32 +++++++++++++++++++++++++++++--- 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/manifest b/manifest index 95d4799b5d..264c838ce7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Experiments\swith\sa\snew\salgorithm\sfor\sconverting\sieee-754\sbinary64\snumbers\ninto\sdecimal. -D 2023-06-30T18:35:43.344 +C Add\sdecimal\srounding\sto\sthe\ssqlite3FpDecode()\sroutine. +D 2023-06-30T19:41:57.301 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -590,7 +590,7 @@ F src/delete.c cd5f5cd06ed0b6a882ec1a8c2a0d73b3cecb28479ad19e9931c4706c5e2182be F src/expr.c 8d1656b65e26af3e34f78e947ac423f0d20c214ed25a67486e433bf16ca6b543 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c a7fcbf7e66d14dbb73cf49f31489ebf66d0e6006c62b95246924a3bae9f37b36 -F src/func.c 699e19ca0dc580a024eb343f3a3abfb646e5438e36d377023d485a3038d789d5 +F src/func.c a6917bd4d4d81ac2320664ea982f85e1a08e566162ef1c776e965fc974b26502 F src/global.c bd0892ade7289f6e20bff44c07d06371f2ff9b53cea359e7854b9b72f65adc30 F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220 F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 @@ -642,7 +642,7 @@ F src/shell.c.in 2c02c819349de410d63fcc0217763dfe5a42dbe58f2d68046d4ea8a376d12c2 F src/sqlite.h.in 3076d78836b6dac53b3ab0875fc8fd15bca8077aad4d33c85336e05af6aef8c7 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4 -F src/sqliteInt.h 6ef127217c54e4707b1ca500508340542ca3c2a5aa08ee7a00aaa4834e68992d +F src/sqliteInt.h 333195d3a62c34af256216e229deb052d87d1068785467a590b56acba0b6c711 F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -705,7 +705,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c 447fd1d981bab37788397c24f980ce555438168f28badde1227441f6d67f1214 +F src/util.c 4a7343a01de122783d6d84c508af15674990578703d5bf55294066978c943f35 F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 @@ -2041,11 +2041,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 7afad1f759f7ceda873c6d869422fd56fe4399c2d24d47ad9bc3b84b06d830d1 -R b84c58ae5f50c9a21c1125fd73d7f10d -T *branch * fp-to-decimal-refactor -T *sym-fp-to-decimal-refactor * -T -sym-trunk * +P e923405e448385085224f9289991b303d86b02763535ea77d6fcee98ba6fc1f2 +R 49ab12ffcc52cccbb997811489070376 U drh -Z aad0befdc05a87e7f362b0543d2a6962 +Z 1293fc1c53a0c963d9c920ddba4be394 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2a088950c5..0ff7f30242 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e923405e448385085224f9289991b303d86b02763535ea77d6fcee98ba6fc1f2 \ No newline at end of file +27871140caa833f0bc0962e44356993938e93dcf81c1074382b1560a3e1aeb61 \ No newline at end of file diff --git a/src/func.c b/src/func.c index 4cf930b208..e41af59b7b 100644 --- a/src/func.c +++ b/src/func.c @@ -2379,10 +2379,12 @@ static void fpdecodeFunc( sqlite3_value **argv ){ double r = sqlite3_value_double(argv[0]); + int n = 15; FpDecode s; char zBuf[50]; - UNUSED_PARAMETER(argc); - sqlite3FpDecode(&s, r); + + if( argc>=2 ) n = sqlite3_value_int(argv[1]); + sqlite3FpDecode(&s, r, n); sqlite3_snprintf(sizeof(zBuf), zBuf, "%c%.*s/%d", s.sign, s.n, s.z, s.iDP); sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); } @@ -2462,6 +2464,7 @@ void sqlite3RegisterBuiltinFunctions(void){ #if 1 /* Temporary prototyping function */ FUNCTION(fpdecode, 1, 0, 0, fpdecodeFunc ), + FUNCTION(fpdecode, 2, 0, 0, fpdecodeFunc ), #endif #ifndef SQLITE_OMIT_FLOATING_POINT diff --git a/src/sqliteInt.h b/src/sqliteInt.h index b4705b5e26..96cac978c7 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4611,7 +4611,7 @@ struct FpDecode { char z[24]; /* Significiant digits */ }; -void sqlite3FpDecode(FpDecode*,double); +void sqlite3FpDecode(FpDecode*,double,int); char *sqlite3MPrintf(sqlite3*,const char*, ...); char *sqlite3VMPrintf(sqlite3*,const char*, va_list); #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) diff --git a/src/util.c b/src/util.c index 29bc8524da..73f834ebbb 100644 --- a/src/util.c +++ b/src/util.c @@ -931,8 +931,13 @@ int sqlite3Atoi(const char *z){ /* ** Decode a floating-point value into an approximate decimal ** representation. +** +** Round the decimal representation to n significant digits if +** n is positive. Or round to -n signficant digits after the +** decimal point if n is negative. No rounding is performed if +** n is zero. */ -void sqlite3FpDecode(FpDecode *p, double r){ +void sqlite3FpDecode(FpDecode *p, double r, int iRound){ int i; u64 v; int e, exp = 0; @@ -983,10 +988,31 @@ void sqlite3FpDecode(FpDecode *p, double r){ while( r<1.0e+17 ){ exp--; r *= 10.0; } } v = (u64)r; - i = sizeof(p->z); + i = sizeof(p->z)-1; while( v ){ p->z[i--] = (v%10) + '0'; v /= 10; } - p->n = sizeof(p->z) - i; + p->n = sizeof(p->z) - 1 - i; p->iDP = p->n + exp; + if( iRound<0 ) iRound = p->iDP - iRound; + if( iRound>0 && iRoundn ){ + char *z = &p->z[i+1]; + p->n = iRound; + if( z[iRound]>='5' ){ + int j = iRound-1; + while( 1 /*exit-by-break*/ ){ + z[j]++; + if( z[j]<='9' ) break; + z[j] = '0'; + if( j==0 ){ + p->z[i--] = '1'; + p->n++; + p->iDP++; + break; + }else{ + j--; + } + } + } + } memmove(p->z, &p->z[i+1], p->n); while( p->n>0 && p->z[p->n-1]=='0' ){ p->n--; } } From aebeabacffbcf6df4ebf0952022fd776fa18ae64 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 30 Jun 2023 23:18:44 +0000 Subject: [PATCH 03/17] Begin cutting over sqlite3FpDecode() into printf(). The code in this check-in gets a lot right, but there are still some formatting discrepencies. FossilOrigin-Name: c134d423e79ba5d3343d7480229822a0c83210f67f12129e0237181f892f97a5 --- manifest | 14 ++-- manifest.uuid | 2 +- src/printf.c | 193 +++++++++++--------------------------------------- src/util.c | 4 ++ 4 files changed, 54 insertions(+), 159 deletions(-) diff --git a/manifest b/manifest index 264c838ce7..1f8f9a511b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sdecimal\srounding\sto\sthe\ssqlite3FpDecode()\sroutine. -D 2023-06-30T19:41:57.301 +C Begin\scutting\sover\ssqlite3FpDecode()\sinto\sprintf().\s\sThe\scode\sin\sthis\ncheck-in\sgets\sa\slot\sright,\sbut\sthere\sare\sstill\ssome\sformatting\ndiscrepencies. +D 2023-06-30T23:18:44.905 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -633,7 +633,7 @@ F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 F src/pragma.c 37b8fb02d090262280c86e1e2654bf59d8dbfbfe8dc6733f2b968a11374c095a F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c d6c4354f8ea0dc06962fbabc4b68c4471a45276a2918c929be00f9f537f69eb1 -F src/printf.c a87473be34fa2acafa27692b8ae078275c7e23360956c93c07ff22f5d609cbd7 +F src/printf.c a3497bdf4e62b8bd56d4a9bb16f9da08ab86951612fcb252206f3f96a8f64557 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 @@ -705,7 +705,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c 4a7343a01de122783d6d84c508af15674990578703d5bf55294066978c943f35 +F src/util.c f7efa26a9cae5efc0d01d008fe4bf0e5e1acc09c1a4a3f30d5097a8c67888d2f F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 @@ -2041,8 +2041,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e923405e448385085224f9289991b303d86b02763535ea77d6fcee98ba6fc1f2 -R 49ab12ffcc52cccbb997811489070376 +P 27871140caa833f0bc0962e44356993938e93dcf81c1074382b1560a3e1aeb61 +R c28c8d21096bb9e27eacb6f123a9309b U drh -Z 1293fc1c53a0c963d9c920ddba4be394 +Z 82b4e7c76d72d8b25d46a6dcbe6ae7dd # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0ff7f30242..96ea34cab0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -27871140caa833f0bc0962e44356993938e93dcf81c1074382b1560a3e1aeb61 \ No newline at end of file +c134d423e79ba5d3343d7480229822a0c83210f67f12129e0237181f892f97a5 \ No newline at end of file diff --git a/src/printf.c b/src/printf.c index 37033ee50a..e804117613 100644 --- a/src/printf.c +++ b/src/printf.c @@ -105,57 +105,6 @@ static const et_info fmtinfo[] = { ** %!S Like %S but prefer the zName over the zAlias */ -/* Floating point constants used for rounding */ -static const double arRound[] = { - 5.0e-01, 5.0e-02, 5.0e-03, 5.0e-04, 5.0e-05, - 5.0e-06, 5.0e-07, 5.0e-08, 5.0e-09, 5.0e-10, -}; - -/* -** If SQLITE_OMIT_FLOATING_POINT is defined, then none of the floating point -** conversions will work. -*/ -#ifndef SQLITE_OMIT_FLOATING_POINT -/* -** "*val" is a double such that 0.1 <= *val < 10.0 -** Return the ascii code for the leading digit of *val, then -** multiply "*val" by 10.0 to renormalize. -** -** Example: -** input: *val = 3.14159 -** output: *val = 1.4159 function return = '3' -** -** The counter *cnt is incremented each time. After counter exceeds -** 16 (the number of significant digits in a 64-bit float) '0' is -** always returned. -*/ -static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ - int digit; - LONGDOUBLE_TYPE d; - if( (*cnt)<=0 ) return '0'; - (*cnt)--; - digit = (int)*val; - d = digit; - digit += '0'; - *val = (*val - d)*10.0; - return (char)digit; -} -#endif /* SQLITE_OMIT_FLOATING_POINT */ - -#ifndef SQLITE_OMIT_FLOATING_POINT -/* -** "*val" is a u64. *msd is a divisor used to extract the -** most significant digit of *val. Extract that most significant -** digit and return it. -*/ -static char et_getdigit_int(u64 *val, u64 *msd){ - u64 x = (*val)/(*msd); - *val -= x*(*msd); - if( *msd>=10 ) *msd /= 10; - return '0' + (char)(x & 15); -} -#endif /* SQLITE_OMIT_FLOATING_POINT */ - /* ** Set the StrAccum object to an error mode. */ @@ -247,20 +196,16 @@ void sqlite3_str_vappendf( u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */ char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ sqlite_uint64 longvalue; /* Value for integer types */ - LONGDOUBLE_TYPE realvalue; /* Value for real types */ - sqlite_uint64 msd; /* Divisor to get most-significant-digit - ** of longvalue */ + double realvalue; /* Value for real types */ const et_info *infop; /* Pointer to the appropriate info structure */ char *zOut; /* Rendering buffer */ int nOut; /* Size of the rendering buffer */ char *zExtra = 0; /* Malloced memory used by some conversion */ -#ifndef SQLITE_OMIT_FLOATING_POINT - int exp, e2; /* exponent of real numbers */ - int nsd; /* Number of significant digits returned */ - double rounder; /* Used for rounding floating point values */ + int exp, e2; /* exponent of real numbers */ + int j; /* Number of significant digits returned */ etByte flag_dp; /* True if decimal point should be shown */ etByte flag_rtz; /* True if trailing zeros should be removed */ -#endif + PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */ char buf[etBUFSIZE]; /* Conversion buffer */ @@ -535,94 +480,51 @@ void sqlite3_str_vappendf( break; case etFLOAT: case etEXP: - case etGENERIC: + case etGENERIC: { + FpDecode s; + if( bArgList ){ realvalue = getDoubleArg(pArgList); }else{ realvalue = va_arg(ap,double); } -#ifdef SQLITE_OMIT_FLOATING_POINT - length = 0; -#else if( precision<0 ) precision = 6; /* Set default precision */ #ifdef SQLITE_FP_PRECISION_LIMIT if( precision>SQLITE_FP_PRECISION_LIMIT ){ precision = SQLITE_FP_PRECISION_LIMIT; } #endif - if( realvalue<0.0 ){ - realvalue = -realvalue; + sqlite3FpDecode(&s, realvalue, xtype==etFLOAT ? -precision : precision); + if( s.isNan ){ + if( flag_zeropad ){ + bufpt = "null"; + length = 4; + }else{ + bufpt = "NaN"; + length = 3; + } + break; + } + if( s.isInf ){ + if( s.sign=='-' ){ + bufpt = "-Inf"; + length = 4; + }else{ + bufpt = "Inf"; + length = 3; + } + break; + } + if( flag_prefix ){ + prefix = flag_prefix; + }else if( s.sign=='-' ){ prefix = '-'; }else{ - prefix = flag_prefix; + prefix = 0; } - exp = 0; - if( xtype==etGENERIC && precision>0 ) precision--; - testcase( precision>0xfff ); - if( realvalue<9.22e+18 - && realvalue==(LONGDOUBLE_TYPE)(longvalue = (u64)realvalue) - ){ - /* Number is a pure integer that can be represented as u64 */ - for(msd=1; msd*10<=longvalue; msd *= 10, exp++){} - if( exp>precision && xtype!=etFLOAT ){ - u64 rnd = msd/2; - int kk = precision; - while( kk-- > 0 ){ rnd /= 10; } - longvalue += rnd; - } - }else{ - msd = 0; - longvalue = 0; /* To prevent a compiler warning */ - idx = precision & 0xfff; - rounder = arRound[idx%10]; - while( idx>=10 ){ rounder *= 1.0e-10; idx -= 10; } - if( xtype==etFLOAT ){ - double rx = (double)realvalue; - sqlite3_uint64 u; - int ex; - memcpy(&u, &rx, sizeof(u)); - ex = -1023 + (int)((u>>52)&0x7ff); - if( precision+(ex/3) < 15 ) rounder += realvalue*3e-16; - realvalue += rounder; - } - if( sqlite3IsNaN((double)realvalue) ){ - if( flag_zeropad ){ - bufpt = "null"; - length = 4; - }else{ - bufpt = "NaN"; - length = 3; - } - break; - } - /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ - if( ALWAYS(realvalue>0.0) ){ - LONGDOUBLE_TYPE scale = 1.0L; - while( realvalue>=1e100*scale && exp<=350){ scale*=1e100L;exp+=100;} - while( realvalue>=1e10*scale && exp<=350 ){ scale*=1e10L; exp+=10; } - while( realvalue>=10.0*scale && exp<=350 ){ scale*=10.0L; exp++; } - realvalue /= scale; - while( realvalue<1e-8 ){ realvalue *= 1e8L; exp-=8; } - while( realvalue<1.0 ){ realvalue *= 10.0L; exp--; } - if( exp>350 ){ - if( flag_zeropad ){ - realvalue = 9.0; - exp = 999; - }else{ - bufpt = buf; - buf[0] = prefix; - memcpy(buf+(prefix!=0),"Inf",4); - length = 3+(prefix!=0); - break; - } - } - if( xtype!=etFLOAT ){ - realvalue += rounder; - if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } - } - } - } + exp = s.iDP-1; + if( xtype==etGENERIC && precision>0 ) precision--; /* ** If the field type is etGENERIC, then convert to either etEXP @@ -642,9 +544,8 @@ void sqlite3_str_vappendf( if( xtype==etEXP ){ e2 = 0; }else{ - e2 = exp; + e2 = s.iDP - 1; } - nsd = 16 + flag_altform2*10; bufpt = buf; { i64 szBufNeeded; /* Size of a temporary buffer needed */ @@ -662,16 +563,12 @@ void sqlite3_str_vappendf( *(bufpt++) = prefix; } /* Digits prior to the decimal point */ + j = 0; if( e2<0 ){ *(bufpt++) = '0'; - }else if( msd>0 ){ - for(; e2>=0; e2--){ - *(bufpt++) = et_getdigit_int(&longvalue,&msd); - if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ','; - } }else{ for(; e2>=0; e2--){ - *(bufpt++) = et_getdigit(&realvalue,&nsd); + *(bufpt++) = s.z[j++]; if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ','; } } @@ -681,19 +578,12 @@ void sqlite3_str_vappendf( } /* "0" digits after the decimal point but before the first ** significant digit of the number */ - for(e2++; e2<0; precision--, e2++){ - assert( precision>0 ); + for(e2++; e2<0 && precision>0; precision--, e2++){ *(bufpt++) = '0'; } /* Significant digits after the decimal point */ - if( msd>0 ){ - while( (precision--)>0 ){ - *(bufpt++) = et_getdigit_int(&longvalue,&msd); - } - }else{ - while( (precision--)>0 ){ - *(bufpt++) = et_getdigit(&realvalue,&nsd); - } + while( (precision--)>0 ){ + *(bufpt++) = jcharset]; if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; @@ -742,8 +633,8 @@ void sqlite3_str_vappendf( while( nPad-- ) bufpt[i++] = '0'; length = width; } -#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */ break; + } case etSIZE: if( !bArgList ){ *(va_arg(ap,int*)) = pAccum->nChar; diff --git a/src/util.c b/src/util.c index 73f834ebbb..cd4eec8d2d 100644 --- a/src/util.c +++ b/src/util.c @@ -949,6 +949,8 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound){ p->n = 1; p->iDP = 1; p->z[0] = '0'; + p->isNan = 0; + p->isInf = 0; return; }else{ p->sign = '+'; @@ -973,6 +975,8 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound){ p->iDP = 3; return; } + p->isNan = p->isInf = 0; + /* 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 From 3f073f9ef83806332ac2a9f62a2c574bf891d048 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 30 Jun 2023 23:42:46 +0000 Subject: [PATCH 04/17] Fix an issue with the "+" attribute in printf(). FossilOrigin-Name: bdfd1eb1d7b4c570a53d58789dac0bf55fdaf8eba515d5a0caf2e57e25b51952 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/printf.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 1f8f9a511b..2b00cbffa5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Begin\scutting\sover\ssqlite3FpDecode()\sinto\sprintf().\s\sThe\scode\sin\sthis\ncheck-in\sgets\sa\slot\sright,\sbut\sthere\sare\sstill\ssome\sformatting\ndiscrepencies. -D 2023-06-30T23:18:44.905 +C Fix\san\sissue\swith\sthe\s"+"\sattribute\sin\sprintf(). +D 2023-06-30T23:42:46.836 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -633,7 +633,7 @@ F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 F src/pragma.c 37b8fb02d090262280c86e1e2654bf59d8dbfbfe8dc6733f2b968a11374c095a F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c d6c4354f8ea0dc06962fbabc4b68c4471a45276a2918c929be00f9f537f69eb1 -F src/printf.c a3497bdf4e62b8bd56d4a9bb16f9da08ab86951612fcb252206f3f96a8f64557 +F src/printf.c 890a111a37f300e171d9df601115774dc964c57b4b1ed7206da849d4861f0188 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 @@ -2041,8 +2041,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 27871140caa833f0bc0962e44356993938e93dcf81c1074382b1560a3e1aeb61 -R c28c8d21096bb9e27eacb6f123a9309b +P c134d423e79ba5d3343d7480229822a0c83210f67f12129e0237181f892f97a5 +R 8f07870f9b2d96881336f99b2799e75b U drh -Z 82b4e7c76d72d8b25d46a6dcbe6ae7dd +Z 159b2bbd6094de9112f8c4b9400c21f4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 96ea34cab0..c5dd949773 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c134d423e79ba5d3343d7480229822a0c83210f67f12129e0237181f892f97a5 \ No newline at end of file +bdfd1eb1d7b4c570a53d58789dac0bf55fdaf8eba515d5a0caf2e57e25b51952 \ No newline at end of file diff --git a/src/printf.c b/src/printf.c index e804117613..b71584af0c 100644 --- a/src/printf.c +++ b/src/printf.c @@ -516,7 +516,7 @@ void sqlite3_str_vappendf( break; } if( flag_prefix ){ - prefix = flag_prefix; + prefix = s.sign; }else if( s.sign=='-' ){ prefix = '-'; }else{ From 42d042e602233be808084e78ccbaba1b497c69a3 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 1 Jul 2023 14:03:50 +0000 Subject: [PATCH 05/17] Progress towards fixing corner-case floating-point formatting issues. FossilOrigin-Name: 1138b8d16edef16d2943d407e289d07d546a14e64c3efa3c94127631e06fafeb --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/printf.c | 17 +++++++++++------ src/util.c | 12 ++++++++++-- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index 2b00cbffa5..253d40cde3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sissue\swith\sthe\s"+"\sattribute\sin\sprintf(). -D 2023-06-30T23:42:46.836 +C Progress\stowards\sfixing\scorner-case\sfloating-point\sformatting\sissues. +D 2023-07-01T14:03:50.030 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -633,7 +633,7 @@ F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 F src/pragma.c 37b8fb02d090262280c86e1e2654bf59d8dbfbfe8dc6733f2b968a11374c095a F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c d6c4354f8ea0dc06962fbabc4b68c4471a45276a2918c929be00f9f537f69eb1 -F src/printf.c 890a111a37f300e171d9df601115774dc964c57b4b1ed7206da849d4861f0188 +F src/printf.c 1291f62b21bbad23d7f9557d5255a0749fb24b86d4bc293fe7210e5a2cdf51b4 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 @@ -705,7 +705,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c f7efa26a9cae5efc0d01d008fe4bf0e5e1acc09c1a4a3f30d5097a8c67888d2f +F src/util.c fc7991df98ed37aca761b40671f39b1359ed4accdcd60546ce58a6e9cb28d6c3 F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 @@ -2041,8 +2041,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P c134d423e79ba5d3343d7480229822a0c83210f67f12129e0237181f892f97a5 -R 8f07870f9b2d96881336f99b2799e75b +P bdfd1eb1d7b4c570a53d58789dac0bf55fdaf8eba515d5a0caf2e57e25b51952 +R fb72667e1d4a1498dbacfca13643a6ba U drh -Z 159b2bbd6094de9112f8c4b9400c21f4 +Z 2c618a760c02dc2da550092622885ef7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c5dd949773..6095020f47 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bdfd1eb1d7b4c570a53d58789dac0bf55fdaf8eba515d5a0caf2e57e25b51952 \ No newline at end of file +1138b8d16edef16d2943d407e289d07d546a14e64c3efa3c94127631e06fafeb \ No newline at end of file diff --git a/src/printf.c b/src/printf.c index b71584af0c..78f59757df 100644 --- a/src/printf.c +++ b/src/printf.c @@ -494,7 +494,11 @@ void sqlite3_str_vappendf( precision = SQLITE_FP_PRECISION_LIMIT; } #endif - sqlite3FpDecode(&s, realvalue, xtype==etFLOAT ? -precision : precision); + switch( xtype ){ + case etFLOAT: sqlite3FpDecode(&s, realvalue, -precision); break; + case etGENERIC: sqlite3FpDecode(&s, realvalue, precision); break; + case etEXP: sqlite3FpDecode(&s, realvalue, precision+1); break; + } if( s.isNan ){ if( flag_zeropad ){ bufpt = "null"; @@ -509,18 +513,19 @@ void sqlite3_str_vappendf( if( s.sign=='-' ){ bufpt = "-Inf"; length = 4; + }else if( flag_prefix=='+' ){ + bufpt = "+Inf"; + length = 4; }else{ bufpt = "Inf"; length = 3; } break; } - if( flag_prefix ){ - prefix = s.sign; - }else if( s.sign=='-' ){ + if( s.sign=='-' ){ prefix = '-'; }else{ - prefix = 0; + prefix = flag_prefix; } exp = s.iDP-1; @@ -568,7 +573,7 @@ void sqlite3_str_vappendf( *(bufpt++) = '0'; }else{ for(; e2>=0; e2--){ - *(bufpt++) = s.z[j++]; + *(bufpt++) = j1 ) *(bufpt++) = ','; } } diff --git a/src/util.c b/src/util.c index cd4eec8d2d..a648dc2d11 100644 --- a/src/util.c +++ b/src/util.c @@ -957,7 +957,7 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound){ } memcpy(&v,&r,8); e = v>>52; - if( e==0x7ff ){ + if( (e&0x7ff)==0x7ff ){ if( v==0x7ff0000000000000L ){ p->isInf = 1; p->isNan = 0; @@ -996,7 +996,15 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound){ while( v ){ p->z[i--] = (v%10) + '0'; v /= 10; } p->n = sizeof(p->z) - 1 - i; p->iDP = p->n + exp; - if( iRound<0 ) iRound = p->iDP - iRound; + if( iRound<0 ){ + iRound = p->iDP - iRound; + if( iRound==0 && p->z[i+1]>='5' ){ + iRound = 1; + p->z[i--] = '0'; + p->n++; + p->iDP++; + } + } if( iRound>0 && iRoundn ){ char *z = &p->z[i+1]; p->n = iRound; From 9ee9444a0adbf8f04081447d70d570f17c1a6e6a Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 1 Jul 2023 15:23:24 +0000 Subject: [PATCH 06/17] Improved response to Infinity and NaN. Remove the termporary fpdecode() function. FossilOrigin-Name: 76ab8ae809a47a66688e2d50c20dc87ce946d82e9ffebb3adda55c451fad07fc --- manifest | 18 ++++++++--------- manifest.uuid | 2 +- src/func.c | 25 ------------------------ src/printf.c | 52 ++++++++++++++++++++++++++----------------------- src/sqliteInt.h | 3 +-- src/util.c | 22 ++++----------------- 6 files changed, 43 insertions(+), 79 deletions(-) diff --git a/manifest b/manifest index 253d40cde3..c7e0fa72ac 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Progress\stowards\sfixing\scorner-case\sfloating-point\sformatting\sissues. -D 2023-07-01T14:03:50.030 +C Improved\sresponse\sto\sInfinity\sand\sNaN.\s\sRemove\sthe\stermporary\sfpdecode()\nfunction. +D 2023-07-01T15:23:24.956 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -590,7 +590,7 @@ F src/delete.c cd5f5cd06ed0b6a882ec1a8c2a0d73b3cecb28479ad19e9931c4706c5e2182be F src/expr.c 8d1656b65e26af3e34f78e947ac423f0d20c214ed25a67486e433bf16ca6b543 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c a7fcbf7e66d14dbb73cf49f31489ebf66d0e6006c62b95246924a3bae9f37b36 -F src/func.c a6917bd4d4d81ac2320664ea982f85e1a08e566162ef1c776e965fc974b26502 +F src/func.c 6b4804738b4d869f40625958b476a8f964d3df65b626e72a530d76051863cf32 F src/global.c bd0892ade7289f6e20bff44c07d06371f2ff9b53cea359e7854b9b72f65adc30 F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220 F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 @@ -633,7 +633,7 @@ F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 F src/pragma.c 37b8fb02d090262280c86e1e2654bf59d8dbfbfe8dc6733f2b968a11374c095a F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c d6c4354f8ea0dc06962fbabc4b68c4471a45276a2918c929be00f9f537f69eb1 -F src/printf.c 1291f62b21bbad23d7f9557d5255a0749fb24b86d4bc293fe7210e5a2cdf51b4 +F src/printf.c 161c522372d34eccb6fbc75b62c40533559ce3f7816a95854dc17e1fa4b4c0d7 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 @@ -642,7 +642,7 @@ F src/shell.c.in 2c02c819349de410d63fcc0217763dfe5a42dbe58f2d68046d4ea8a376d12c2 F src/sqlite.h.in 3076d78836b6dac53b3ab0875fc8fd15bca8077aad4d33c85336e05af6aef8c7 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4 -F src/sqliteInt.h 333195d3a62c34af256216e229deb052d87d1068785467a590b56acba0b6c711 +F src/sqliteInt.h 8c1922b2780624225223fd407f995b04392175d33dfcbbca37fe1f00e1abfd0a F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -705,7 +705,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c fc7991df98ed37aca761b40671f39b1359ed4accdcd60546ce58a6e9cb28d6c3 +F src/util.c 9cddb6a7b31656609e8585475c30e6148a442273b73bfbb3eaabd56e04f157dd F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 @@ -2041,8 +2041,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P bdfd1eb1d7b4c570a53d58789dac0bf55fdaf8eba515d5a0caf2e57e25b51952 -R fb72667e1d4a1498dbacfca13643a6ba +P 1138b8d16edef16d2943d407e289d07d546a14e64c3efa3c94127631e06fafeb +R 9f9cd9357c9913af2aa19cc840191091 U drh -Z 2c618a760c02dc2da550092622885ef7 +Z 1a305ec6bce26e8306c1bccf245f209e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6095020f47..2f293ccf31 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1138b8d16edef16d2943d407e289d07d546a14e64c3efa3c94127631e06fafeb \ No newline at end of file +76ab8ae809a47a66688e2d50c20dc87ce946d82e9ffebb3adda55c451fad07fc \ No newline at end of file diff --git a/src/func.c b/src/func.c index e41af59b7b..b243591863 100644 --- a/src/func.c +++ b/src/func.c @@ -2371,25 +2371,6 @@ static void signFunc( sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0); } - -#if 1 /* Temporary prototyping logic */ -static void fpdecodeFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - double r = sqlite3_value_double(argv[0]); - int n = 15; - FpDecode s; - char zBuf[50]; - - if( argc>=2 ) n = sqlite3_value_int(argv[1]); - sqlite3FpDecode(&s, r, n); - sqlite3_snprintf(sizeof(zBuf), zBuf, "%c%.*s/%d", s.sign, s.n, s.z, s.iDP); - sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); -} -#endif /* Temporary prototyping logic */ - /* ** All of the FuncDef structures in the aBuiltinFunc[] array above ** to the global function hash table. This occurs at start-time (as @@ -2461,12 +2442,6 @@ void sqlite3RegisterBuiltinFunctions(void){ FUNCTION(unicode, 1, 0, 0, unicodeFunc ), FUNCTION(char, -1, 0, 0, charFunc ), FUNCTION(abs, 1, 0, 0, absFunc ), - -#if 1 /* Temporary prototyping function */ - FUNCTION(fpdecode, 1, 0, 0, fpdecodeFunc ), - FUNCTION(fpdecode, 2, 0, 0, fpdecodeFunc ), -#endif - #ifndef SQLITE_OMIT_FLOATING_POINT FUNCTION(round, 1, 0, 0, roundFunc ), FUNCTION(round, 2, 0, 0, roundFunc ), diff --git a/src/printf.c b/src/printf.c index 78f59757df..4de06c1162 100644 --- a/src/printf.c +++ b/src/printf.c @@ -482,6 +482,7 @@ void sqlite3_str_vappendf( case etEXP: case etGENERIC: { FpDecode s; + int iRound; if( bArgList ){ realvalue = getDoubleArg(pArgList); @@ -494,33 +495,36 @@ void sqlite3_str_vappendf( precision = SQLITE_FP_PRECISION_LIMIT; } #endif - switch( xtype ){ - case etFLOAT: sqlite3FpDecode(&s, realvalue, -precision); break; - case etGENERIC: sqlite3FpDecode(&s, realvalue, precision); break; - case etEXP: sqlite3FpDecode(&s, realvalue, precision+1); break; + if( xtype==etFLOAT ){ + iRound = -precision; + }else if( xtype==etGENERIC ){ + iRound = precision; + }else{ + iRound = precision+1; } - if( s.isNan ){ - if( flag_zeropad ){ - bufpt = "null"; - length = 4; + sqlite3FpDecode(&s, realvalue, iRound); + if( s.isSpecial ){ + if( s.isSpecial==2 ){ + bufpt = flag_zeropad ? "null" : "NaN"; + length = sqlite3Strlen30(bufpt); + break; + }else if( flag_zeropad ){ + s.z[0] = '9'; + s.iDP = 1000; + s.n = 1; }else{ - bufpt = "NaN"; - length = 3; + memcpy(buf, "-Inf", 5); + bufpt = buf; + if( s.sign=='-' ){ + /* no-op */ + }else if( flag_prefix ){ + buf[0] = flag_prefix; + }else{ + bufpt++; + } + length = sqlite3Strlen30(bufpt); + break; } - break; - } - if( s.isInf ){ - if( s.sign=='-' ){ - bufpt = "-Inf"; - length = 4; - }else if( flag_prefix=='+' ){ - bufpt = "+Inf"; - length = 4; - }else{ - bufpt = "Inf"; - length = 3; - } - break; } if( s.sign=='-' ){ prefix = '-'; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 96cac978c7..011a44bcb9 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4604,8 +4604,7 @@ struct PrintfArguments { */ struct FpDecode { char sign; /* '+' or '-' */ - char isNan; /* True if not-a-number */ - char isInf; /* True if infinity */ + char isSpecial; /* 1: Infinity 2: NaN */ int n; /* Significant digits in the decode */ int iDP; /* Location of the decimal point */ char z[24]; /* Significiant digits */ diff --git a/src/util.c b/src/util.c index a648dc2d11..0950d8427a 100644 --- a/src/util.c +++ b/src/util.c @@ -941,6 +941,7 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound){ int i; u64 v; int e, exp = 0; + p->isSpecial = 0; if( r<0.0 ){ p->sign = '-'; r = -r; @@ -949,8 +950,6 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound){ p->n = 1; p->iDP = 1; p->z[0] = '0'; - p->isNan = 0; - p->isInf = 0; return; }else{ p->sign = '+'; @@ -958,24 +957,11 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound){ memcpy(&v,&r,8); e = v>>52; if( (e&0x7ff)==0x7ff ){ - if( v==0x7ff0000000000000L ){ - p->isInf = 1; - p->isNan = 0; - p->z[0] = 'I'; - p->z[1] = 'n'; - p->z[2] = 'f'; - }else{ - p->isInf = 0; - p->isNan = 1; - p->z[0] = 'N'; - p->z[1] = 'a'; - p->z[2] = 'N'; - } - p->n = 3; - p->iDP = 3; + p->isSpecial = 1 + (v!=0x7ff0000000000000L); + p->n = 0; + p->iDP = 0; return; } - p->isNan = p->isInf = 0; /* 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 From 17c20bb15e5f51e12efd95fd3dc8806f939d8af8 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 1 Jul 2023 17:56:00 +0000 Subject: [PATCH 07/17] Improved rounding policy. FossilOrigin-Name: 6f1122e942b8269552daaf13d647d200d8546ec25f36310d67037c6b58d09976 --- manifest | 20 ++++++++++---------- manifest.uuid | 2 +- src/func.c | 2 +- src/printf.c | 2 +- src/sqliteInt.h | 2 +- src/util.c | 7 ++++--- test/round1.test | 4 ++-- 7 files changed, 20 insertions(+), 19 deletions(-) diff --git a/manifest b/manifest index c7e0fa72ac..5d34c0afa1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improved\sresponse\sto\sInfinity\sand\sNaN.\s\sRemove\sthe\stermporary\sfpdecode()\nfunction. -D 2023-07-01T15:23:24.956 +C Improved\srounding\spolicy. +D 2023-07-01T17:56:00.664 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -590,7 +590,7 @@ F src/delete.c cd5f5cd06ed0b6a882ec1a8c2a0d73b3cecb28479ad19e9931c4706c5e2182be F src/expr.c 8d1656b65e26af3e34f78e947ac423f0d20c214ed25a67486e433bf16ca6b543 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c a7fcbf7e66d14dbb73cf49f31489ebf66d0e6006c62b95246924a3bae9f37b36 -F src/func.c 6b4804738b4d869f40625958b476a8f964d3df65b626e72a530d76051863cf32 +F src/func.c 02ad65dea00c07db33df86ad649a81a503ac7b8a259e89d00e7a81e16ae9d2d6 F src/global.c bd0892ade7289f6e20bff44c07d06371f2ff9b53cea359e7854b9b72f65adc30 F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220 F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 @@ -633,7 +633,7 @@ F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 F src/pragma.c 37b8fb02d090262280c86e1e2654bf59d8dbfbfe8dc6733f2b968a11374c095a F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c d6c4354f8ea0dc06962fbabc4b68c4471a45276a2918c929be00f9f537f69eb1 -F src/printf.c 161c522372d34eccb6fbc75b62c40533559ce3f7816a95854dc17e1fa4b4c0d7 +F src/printf.c 1d2d5ba8c2d6446b89ca4afeeb5e99f8c09ca8cfd6947e33493112771ea7532a F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 @@ -642,7 +642,7 @@ F src/shell.c.in 2c02c819349de410d63fcc0217763dfe5a42dbe58f2d68046d4ea8a376d12c2 F src/sqlite.h.in 3076d78836b6dac53b3ab0875fc8fd15bca8077aad4d33c85336e05af6aef8c7 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4 -F src/sqliteInt.h 8c1922b2780624225223fd407f995b04392175d33dfcbbca37fe1f00e1abfd0a +F src/sqliteInt.h 3d4125cc1c38777f307e7d374890f085d09f6e15298e09a4f74919744ec2ad62 F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -705,7 +705,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c 9cddb6a7b31656609e8585475c30e6148a442273b73bfbb3eaabd56e04f157dd +F src/util.c 87e6762b026cd733312838b0e9b9acb43d3a3c88c5e612e8077a7320c2bc1692 F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 @@ -1399,7 +1399,7 @@ F test/returningfault.test ae4c4b5e8745813287a359d9ccdb9d5c883c2e68afb18fb076793 F test/rollback.test 06680159bc6746d0f26276e339e3ae2f951c64812468308838e0a3362d911eaa F test/rollback2.test 3f3a4e20401825017df7e7671e9f31b6de5fae5620c2b9b49917f52f8c160a8f F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a -F test/round1.test 1bb32cf3fc505eed9e86b5e523d07e15d4428189665524587512fbcc85d114bb +F test/round1.test 29c3c9039936ed024d672f003c4d35ee11c14c0acb75c5f7d6188ff16190cfd4 F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test e29025be95baf6b32f0d5edef59a7633028325896a98f1caa8019559ca910350 @@ -2041,8 +2041,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 1138b8d16edef16d2943d407e289d07d546a14e64c3efa3c94127631e06fafeb -R 9f9cd9357c9913af2aa19cc840191091 +P 76ab8ae809a47a66688e2d50c20dc87ce946d82e9ffebb3adda55c451fad07fc +R bac28918fe04cf4bb6c5ec2b0ffa1827 U drh -Z 1a305ec6bce26e8306c1bccf245f209e +Z 6ffcf546adee0eb6c01fe1a8fc34f949 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2f293ccf31..f7aec02361 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -76ab8ae809a47a66688e2d50c20dc87ce946d82e9ffebb3adda55c451fad07fc \ No newline at end of file +6f1122e942b8269552daaf13d647d200d8546ec25f36310d67037c6b58d09976 \ No newline at end of file diff --git a/src/func.c b/src/func.c index b243591863..07ada2ef6e 100644 --- a/src/func.c +++ b/src/func.c @@ -461,7 +461,7 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ }else if( n==0 ){ r = (double)((sqlite_int64)(r+(r<0?-0.5:+0.5))); }else{ - zBuf = sqlite3_mprintf("%.*f",n,r); + zBuf = sqlite3_mprintf("%!.*f",n,r); if( zBuf==0 ){ sqlite3_result_error_nomem(context); return; diff --git a/src/printf.c b/src/printf.c index 4de06c1162..f00ad4cb2e 100644 --- a/src/printf.c +++ b/src/printf.c @@ -502,7 +502,7 @@ void sqlite3_str_vappendf( }else{ iRound = precision+1; } - sqlite3FpDecode(&s, realvalue, iRound); + sqlite3FpDecode(&s, realvalue, iRound, flag_altform2 ? 26 : 16); if( s.isSpecial ){ if( s.isSpecial==2 ){ bufpt = flag_zeropad ? "null" : "NaN"; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 011a44bcb9..5662ab4fd2 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4610,7 +4610,7 @@ struct FpDecode { char z[24]; /* Significiant digits */ }; -void sqlite3FpDecode(FpDecode*,double,int); +void sqlite3FpDecode(FpDecode*,double,int,int); char *sqlite3MPrintf(sqlite3*,const char*, ...); char *sqlite3VMPrintf(sqlite3*,const char*, va_list); #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) diff --git a/src/util.c b/src/util.c index 0950d8427a..41359ea2a2 100644 --- a/src/util.c +++ b/src/util.c @@ -937,7 +937,7 @@ int sqlite3Atoi(const char *z){ ** decimal point if n is negative. No rounding is performed if ** n is zero. */ -void sqlite3FpDecode(FpDecode *p, double r, int iRound){ +void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ int i; u64 v; int e, exp = 0; @@ -972,7 +972,7 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound){ while( r>=1.0e+119 ){ exp+=100; r /= 1.0e+100; } while( r>=1.0e+29 ){ exp+=10; r /= 1.0e+10; } while( r>=1.0e+19 ){ exp++; r /= 10.0; } - }else if( r<1.0e+17 ){ + }else{ while( r<1.0e-97 ){ exp-=100; r *= 1.0e+100; } while( r<1.0e+07 ){ exp-=10; r *= 1.0e+10; } while( r<1.0e+17 ){ exp--; r *= 10.0; } @@ -991,8 +991,9 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound){ p->iDP++; } } - if( iRound>0 && iRoundn ){ + if( iRound>0 && (iRoundn || p->n>mxRound) ){ char *z = &p->z[i+1]; + if( iRound>mxRound ) iRound = mxRound; p->n = iRound; if( z[iRound]>='5' ){ int j = iRound-1; diff --git a/test/round1.test b/test/round1.test index 2244a399ab..2857593be8 100644 --- a/test/round1.test +++ b/test/round1.test @@ -34,8 +34,8 @@ for {set iTest 1} {$iTest<=50000} {incr iTest} { set r2 $x1.$x4 set ans [string trimright $r2 0] if {[string match *. $ans]} {set ans ${ans}0} - do_test $iTest/$n/${r}5=>$ans { - set x [db one "SELECT round(${r}5,$n)"] + do_test $iTest/$n/${r}6=>$ans { + set x [db one "SELECT round(${r}6,$n)"] } $ans } From 453be55b85d98ab55d7398cbebf81f413d1d7794 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 1 Jul 2023 18:33:26 +0000 Subject: [PATCH 08/17] Reinstate the fpdecode() SQL function for testing, but only when compiled with SQLITE_DEBUG. FossilOrigin-Name: 07eab52e0801bb0e4743b304a06ad16e18cdf8eaa18c0faf8d47a1f5d8576ea0 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/func.c | 34 ++++++++++++++++++++++++++++++++++ src/printf.c | 2 +- 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 5d34c0afa1..d3e50a606b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improved\srounding\spolicy. -D 2023-07-01T17:56:00.664 +C Reinstate\sthe\sfpdecode()\sSQL\sfunction\sfor\stesting,\sbut\sonly\swhen\scompiled\nwith\sSQLITE_DEBUG. +D 2023-07-01T18:33:26.856 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -590,7 +590,7 @@ F src/delete.c cd5f5cd06ed0b6a882ec1a8c2a0d73b3cecb28479ad19e9931c4706c5e2182be F src/expr.c 8d1656b65e26af3e34f78e947ac423f0d20c214ed25a67486e433bf16ca6b543 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c a7fcbf7e66d14dbb73cf49f31489ebf66d0e6006c62b95246924a3bae9f37b36 -F src/func.c 02ad65dea00c07db33df86ad649a81a503ac7b8a259e89d00e7a81e16ae9d2d6 +F src/func.c 6028c160f693bdd018b651b5468a0a8e790f4e01e200796916b2d10a5d3237aa F src/global.c bd0892ade7289f6e20bff44c07d06371f2ff9b53cea359e7854b9b72f65adc30 F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220 F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 @@ -633,7 +633,7 @@ F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 F src/pragma.c 37b8fb02d090262280c86e1e2654bf59d8dbfbfe8dc6733f2b968a11374c095a F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c d6c4354f8ea0dc06962fbabc4b68c4471a45276a2918c929be00f9f537f69eb1 -F src/printf.c 1d2d5ba8c2d6446b89ca4afeeb5e99f8c09ca8cfd6947e33493112771ea7532a +F src/printf.c 84b7b4b647f336934a5ab2e7f0c52555833cc0778d2d60e016cca52ee8c6cd8f F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 @@ -2041,8 +2041,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 76ab8ae809a47a66688e2d50c20dc87ce946d82e9ffebb3adda55c451fad07fc -R bac28918fe04cf4bb6c5ec2b0ffa1827 +P 6f1122e942b8269552daaf13d647d200d8546ec25f36310d67037c6b58d09976 +R eef1ed887e702bb26e5ed7046220e6a0 U drh -Z 6ffcf546adee0eb6c01fe1a8fc34f949 +Z 09a504821196951b22da7c1db783401d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f7aec02361..9eae2022f6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6f1122e942b8269552daaf13d647d200d8546ec25f36310d67037c6b58d09976 \ No newline at end of file +07eab52e0801bb0e4743b304a06ad16e18cdf8eaa18c0faf8d47a1f5d8576ea0 \ No newline at end of file diff --git a/src/func.c b/src/func.c index 07ada2ef6e..c6a49de613 100644 --- a/src/func.c +++ b/src/func.c @@ -2371,6 +2371,37 @@ static void signFunc( sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0); } +#ifdef SQLITE_DEBUG +/* +** Implementation of fpdecode(x,y,z) function. +** +** x is a real number that is to be decoded. y is the precision. +** z is the maximum real precision. +*/ +static void fpdecodeFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + FpDecode s; + double x; + int y, z; + char zBuf[100]; + UNUSED_PARAMETER(argc); + assert( argc==3 ); + x = sqlite3_value_double(argv[0]); + y = sqlite3_value_int(argv[1]); + z = sqlite3_value_int(argv[2]); + sqlite3FpDecode(&s, x, y, z); + if( s.isSpecial==2 ){ + sqlite3_snprintf(sizeof(zBuf), zBuf, "NaN"); + }else{ + sqlite3_snprintf(sizeof(zBuf), zBuf, "%c%.*s/%d", s.sign, s.n, s.z, s.iDP); + } + sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); +} +#endif /* SQLITE_DEBUG */ + /* ** All of the FuncDef structures in the aBuiltinFunc[] array above ** to the global function hash table. This occurs at start-time (as @@ -2442,6 +2473,9 @@ void sqlite3RegisterBuiltinFunctions(void){ FUNCTION(unicode, 1, 0, 0, unicodeFunc ), FUNCTION(char, -1, 0, 0, charFunc ), FUNCTION(abs, 1, 0, 0, absFunc ), +#ifdef SQLITE_DEBUG + FUNCTION(fpdecode, 3, 0, 0, fpdecodeFunc ), +#endif #ifndef SQLITE_OMIT_FLOATING_POINT FUNCTION(round, 1, 0, 0, roundFunc ), FUNCTION(round, 2, 0, 0, roundFunc ), diff --git a/src/printf.c b/src/printf.c index f00ad4cb2e..3fb1a322a0 100644 --- a/src/printf.c +++ b/src/printf.c @@ -202,7 +202,6 @@ void sqlite3_str_vappendf( int nOut; /* Size of the rendering buffer */ char *zExtra = 0; /* Malloced memory used by some conversion */ int exp, e2; /* exponent of real numbers */ - int j; /* Number of significant digits returned */ etByte flag_dp; /* True if decimal point should be shown */ etByte flag_rtz; /* True if trailing zeros should be removed */ @@ -483,6 +482,7 @@ void sqlite3_str_vappendf( case etGENERIC: { FpDecode s; int iRound; + int j; if( bArgList ){ realvalue = getDoubleArg(pArgList); From fecdff43545a50c14634ed087682635d1405f440 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 1 Jul 2023 20:21:27 +0000 Subject: [PATCH 09/17] This check-in adds the use of "long double" to sqlite3FpDecode() which when run on x86 using a compiler that translates "long double" into binary80, causes all legacy tests to pass. But on a system where "long double" is just an alias for "double", some of the tests are still failing. FossilOrigin-Name: ce06982f880339cf46704e95c907249827c3e44af2b9420005200ca8abd3f371 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/util.c | 24 +++++++++++++----------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/manifest b/manifest index d3e50a606b..60a8a88fd4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Reinstate\sthe\sfpdecode()\sSQL\sfunction\sfor\stesting,\sbut\sonly\swhen\scompiled\nwith\sSQLITE_DEBUG. -D 2023-07-01T18:33:26.856 +C This\scheck-in\sadds\sthe\suse\sof\s"long\sdouble"\sto\ssqlite3FpDecode()\swhich\swhen\nrun\son\sx86\susing\sa\scompiler\sthat\stranslates\s"long\sdouble"\sinto\sbinary80,\ncauses\sall\slegacy\stests\sto\spass.\s\sBut\son\sa\ssystem\swhere\s"long\sdouble"\sis\sjust\nan\salias\sfor\s"double",\ssome\sof\sthe\stests\sare\sstill\sfailing. +D 2023-07-01T20:21:27.087 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -705,7 +705,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c 87e6762b026cd733312838b0e9b9acb43d3a3c88c5e612e8077a7320c2bc1692 +F src/util.c d68692390be083744bd0dfafc797472b0d304743040220e18b93bc9bad8c8d6e F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 @@ -2041,8 +2041,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 6f1122e942b8269552daaf13d647d200d8546ec25f36310d67037c6b58d09976 -R eef1ed887e702bb26e5ed7046220e6a0 +P 07eab52e0801bb0e4743b304a06ad16e18cdf8eaa18c0faf8d47a1f5d8576ea0 +R c4051b06b266c29a490b4cec354913fc U drh -Z 09a504821196951b22da7c1db783401d +Z 95eef79511e3e10371b4b76031a5a06d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9eae2022f6..d2f8b9d453 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -07eab52e0801bb0e4743b304a06ad16e18cdf8eaa18c0faf8d47a1f5d8576ea0 \ No newline at end of file +ce06982f880339cf46704e95c907249827c3e44af2b9420005200ca8abd3f371 \ No newline at end of file diff --git a/src/util.c b/src/util.c index 41359ea2a2..a560f26133 100644 --- a/src/util.c +++ b/src/util.c @@ -937,15 +937,16 @@ int sqlite3Atoi(const char *z){ ** decimal point if n is negative. No rounding is performed if ** n is zero. */ -void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ +void sqlite3FpDecode(FpDecode *p, double rr, int iRound, int mxRound){ int i; u64 v; int e, exp = 0; + long double r; p->isSpecial = 0; - if( r<0.0 ){ + if( rr<0.0 ){ p->sign = '-'; - r = -r; - }else if( r==0.0 ){ + rr = -rr; + }else if( rr==0.0 ){ p->sign = '+'; p->n = 1; p->iDP = 1; @@ -954,7 +955,7 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ }else{ p->sign = '+'; } - memcpy(&v,&r,8); + memcpy(&v,&rr,8); e = v>>52; if( (e&0x7ff)==0x7ff ){ p->isSpecial = 1 + (v!=0x7ff0000000000000L); @@ -962,6 +963,7 @@ void sqlite3FpDecode(FpDecode *p, double r, 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 @@ -969,13 +971,13 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ ** an unsigned 64-bit integer v, and extract digits from v. */ if( r>=1.0e+19 ){ - while( r>=1.0e+119 ){ exp+=100; r /= 1.0e+100; } - while( r>=1.0e+29 ){ exp+=10; r /= 1.0e+10; } - while( r>=1.0e+19 ){ exp++; r /= 10.0; } + 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; } }else{ - while( r<1.0e-97 ){ exp-=100; r *= 1.0e+100; } - while( r<1.0e+07 ){ exp-=10; r *= 1.0e+10; } - while( r<1.0e+17 ){ exp--; r *= 10.0; } + 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; } } v = (u64)r; i = sizeof(p->z)-1; From c27bda0b7911186d1e11c8ffcd55818cfa211009 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 3 Jul 2023 00:40:37 +0000 Subject: [PATCH 10/17] 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 --- manifest | 15 +++++++----- manifest.uuid | 2 +- src/util.c | 64 ++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 55 insertions(+), 26 deletions(-) diff --git a/manifest b/manifest index 60a8a88fd4..f969dca2f8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C This\scheck-in\sadds\sthe\suse\sof\s"long\sdouble"\sto\ssqlite3FpDecode()\swhich\swhen\nrun\son\sx86\susing\sa\scompiler\sthat\stranslates\s"long\sdouble"\sinto\sbinary80,\ncauses\sall\slegacy\stests\sto\spass.\s\sBut\son\sa\ssystem\swhere\s"long\sdouble"\sis\sjust\nan\salias\sfor\s"double",\ssome\sof\sthe\stests\sare\sstill\sfailing. -D 2023-07-01T20:21:27.087 +C An\sidea\sfor\simproving\saccuracy\sof\sfp-to-decimal\sconversion\sfor\ssystems\sthat\ndo\snot\shave\s"long\sdouble".\s\sDoes\snot\squite\swork.\s\sRetained\sonly\sfor\ndocumentation\spurposes. +D 2023-07-03T00:40:37.359 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -705,7 +705,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c d68692390be083744bd0dfafc797472b0d304743040220e18b93bc9bad8c8d6e +F src/util.c 12741de9969af5134488b7c89a97ecc22144f93f62c6255f797abb9b3121608a F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 @@ -2041,8 +2041,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 07eab52e0801bb0e4743b304a06ad16e18cdf8eaa18c0faf8d47a1f5d8576ea0 -R c4051b06b266c29a490b4cec354913fc +P ce06982f880339cf46704e95c907249827c3e44af2b9420005200ca8abd3f371 +R ae7783ab51788007ac81a8600cb6b0a8 +T *branch * fp-to-decimal-branch1 +T *sym-fp-to-decimal-branch1 * +T -sym-fp-to-decimal-refactor * U drh -Z 95eef79511e3e10371b4b76031a5a06d +Z 0f216e6dd96bccb8187eb2a64c983082 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d2f8b9d453..d8fd936394 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ce06982f880339cf46704e95c907249827c3e44af2b9420005200ca8abd3f371 \ No newline at end of file +702243333843219f8904ee0fd12283080d6c6b3fc1ffb36c534cdefb3563c00d \ No newline at end of file diff --git a/src/util.c b/src/util.c index a560f26133..9bc37c4f9a 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( r20.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; From c8885d42fe4799a265c69f4a6bbd4cdb3cf4daea Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 3 Jul 2023 09:07:23 +0000 Subject: [PATCH 11/17] Replace the dodgy error estimating logic in the previous check-in with full-up Dekker double-double multiplication, and this idea works much better. There are still a few inaccuracies, but it is much closer. FossilOrigin-Name: 4fa6938dac2d3d813a37664053db31451a2a065f78dd212488f5f7f8d583ddc5 --- manifest | 17 +++++------ manifest.uuid | 2 +- src/util.c | 77 +++++++++++++++++++++++++++++++++++++------------ test/atof1.test | 6 ++-- 4 files changed, 69 insertions(+), 33 deletions(-) diff --git a/manifest b/manifest index f969dca2f8..a420e218ba 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C An\sidea\sfor\simproving\saccuracy\sof\sfp-to-decimal\sconversion\sfor\ssystems\sthat\ndo\snot\shave\s"long\sdouble".\s\sDoes\snot\squite\swork.\s\sRetained\sonly\sfor\ndocumentation\spurposes. -D 2023-07-03T00:40:37.359 +C Replace\sthe\sdodgy\serror\sestimating\slogic\sin\sthe\sprevious\scheck-in\swith\nfull-up\sDekker\sdouble-double\smultiplication,\sand\sthis\sidea\sworks\smuch\sbetter.\nThere\sare\sstill\sa\sfew\sinaccuracies,\sbut\sit\sis\smuch\scloser. +D 2023-07-03T09:07:23.117 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -705,7 +705,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c 12741de9969af5134488b7c89a97ecc22144f93f62c6255f797abb9b3121608a +F src/util.c 1feadae2da54a59ab7ec82d4b94ed22f94bb5d4df25e41e73c05c8f3c1328607 F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 @@ -775,7 +775,7 @@ F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7 F test/async4.test 1787e3952128aa10238bf39945126de7ca23685a F test/async5.test 383ab533fdb9f7ad228cc99ee66e1acb34cc0dc0 -F test/atof1.test 1f24a6f16e44892848298bd8e488159d4a60cf47be003d32747a136103c9bbac +F test/atof1.test b4dbc37187649e23ba92e06e0d2c076304516967b5a8b0af50f8e4f2323e654c F test/atomic.test 065a453dde33c77ff586d91ccaa6ed419829d492dbb1a5694b8a09f3f9d7d061 F test/atomic2.test b6863b4aa552543874f80b42fb3063f1c8c2e3d8e56b6562f00a3cc347b5c1da F test/atrc.c c388fac43dbba05c804432a7135ae688b32e8f25818e9994ffba4b64cf60c27c @@ -2041,11 +2041,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P ce06982f880339cf46704e95c907249827c3e44af2b9420005200ca8abd3f371 -R ae7783ab51788007ac81a8600cb6b0a8 -T *branch * fp-to-decimal-branch1 -T *sym-fp-to-decimal-branch1 * -T -sym-fp-to-decimal-refactor * +P 702243333843219f8904ee0fd12283080d6c6b3fc1ffb36c534cdefb3563c00d +R 0bc2956a2833c937de8e3462962e6784 U drh -Z 0f216e6dd96bccb8187eb2a64c983082 +Z 2f85ae3f7bbd29aeb71b560da07ddd48 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d8fd936394..8bb5c10510 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -702243333843219f8904ee0fd12283080d6c6b3fc1ffb36c534cdefb3563c00d \ No newline at end of file +4fa6938dac2d3d813a37664053db31451a2a065f78dd212488f5f7f8d583ddc5 \ No newline at end of file diff --git a/src/util.c b/src/util.c index 9bc37c4f9a..b7d6bd58e8 100644 --- a/src/util.c +++ b/src/util.c @@ -928,6 +928,31 @@ int sqlite3Atoi(const char *z){ return x; } +/* Double-Double multiplication. *(z,zz) = (x,xx) * (y,yy) +** +** Reference: +** T. J. Dekker, "A Floating-Point Technique for Extending the +** Available Precision". 1971-07-26. +*/ +static void mul2( + double x, double xx, + double y, double yy, + double *z, double *zz +){ + double hx, tx, hy, ty, p, q, c, cc; + p = 67108865.0 * x; + hx = x - p + p; tx = x - hx; + p = 67108865.0 * y; + hy = y - p + p; ty = y - hy; + p = hx*hy; + q = hx*ty + tx*hy; + c = p+q; + cc = p - c + q + tx*ty; + cc = x*yy + xx*y + cc; + *z = c + cc; + *zz = c - *z + cc; +} + /* ** Decode a floating-point value into an approximate decimal ** representation. @@ -969,10 +994,7 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ /* Multiply r by powers of ten until it lands somewhere in between ** 1.0e+19 and 1.0e+17. */ - if( sizeof(long double)>8 - || (v & 0x0000000000ffffffLL)==0 - || (v & 0xffffffffff000000LL)==0 - ){ + if( sizeof(long double)>8 && 0 ){ long double rr = r; if( rr>=1.0e+19 ){ while( rr>=1.0e+119L ){ exp+=100; rr *= 1.0e-100L; } @@ -985,23 +1007,40 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ } v = (u64)rr; }else{ - double r2, r3; - v &= 0xffffffffff000000LL; - memcpy(&r2, &v, 8); - assert( r20.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; } + double rr = 0.0; + if( r>=1.0e+19 ){ + while( r>=1.0e+119 ){ + if( r>=1.0e+300 ){ + exp += 300; + r *= 1.0e-300; + break; + } + exp += 100; + mul2(r, rr, 1.0e-100, -1.99918998026028836196e-117, &r, &rr); + } + while( r>=1.0e+29 ){ + exp += 10; + mul2(r,rr, 1.0e-10, -3.6432197315497741579e-27, &r, &rr); + } + while( r>=1.0e+19 ){ + exp += 1; + mul2(r,rr, 1.0e-01, -5.5511151231257827021e-18, &r, &rr); + } }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; } + while( r<1.0e-98 ){ + exp -= 100; + mul2(r, rr, 1.0e+100, -1.5902891109759918046, &r, &rr); + } + while( r<1.0e+08 ){ + exp -= 10; + mul2(r, rr, 1.0e+10, 0.0, &r, &rr); + } + while( r<1.0e+18 ){ + exp -= 1; + mul2(r, rr, 1.0e+01, 0.0, &r, &rr); + } } - v = (u64)(r2+r3); + v = (u64)(r)+(u64)(rr); } diff --git a/test/atof1.test b/test/atof1.test index 5959c5d5b9..ecfbdd33ee 100644 --- a/test/atof1.test +++ b/test/atof1.test @@ -51,10 +51,10 @@ for {set i 1} {$i<20000} {incr i} { set y [db eval {SELECT $x=CAST(quote($x) AS real)}] if {!$y} { db eval {SELECT real2hex($x) a, real2hex(CAST(quote($x) AS real)) b} {} - puts "\nIN: $a $xf" - puts [format {QUOTE: %16s %s} {} [db eval {SELECT quote($x)}]] + puts "\n!IN: $a $xf" + puts [format {!QUOTE: %16s %s} {} [db eval {SELECT quote($x)}]] db eval {SELECT CAST(quote($x) AS real) c} {} - puts "OUT: $b [format %.32e $c]" + puts "!OUT: $b [format %.32e $c]" } set y } {1} From 37b188fbca861daa26f649bfaa4c42771ff315d7 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 3 Jul 2023 10:00:38 +0000 Subject: [PATCH 12/17] Fix one constant in the normalization logic. Improved error output from atof1.test. FossilOrigin-Name: d3c48807100a358a70fdd799c8935eba1b765ace2e1ddea4475fd673006cb6da --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/util.c | 2 +- test/atof1.test | 8 +++++++- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index a420e218ba..5ae086cfa2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Replace\sthe\sdodgy\serror\sestimating\slogic\sin\sthe\sprevious\scheck-in\swith\nfull-up\sDekker\sdouble-double\smultiplication,\sand\sthis\sidea\sworks\smuch\sbetter.\nThere\sare\sstill\sa\sfew\sinaccuracies,\sbut\sit\sis\smuch\scloser. -D 2023-07-03T09:07:23.117 +C Fix\sone\sconstant\sin\sthe\snormalization\slogic.\s\sImproved\serror\soutput\nfrom\satof1.test. +D 2023-07-03T10:00:38.019 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -705,7 +705,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c 1feadae2da54a59ab7ec82d4b94ed22f94bb5d4df25e41e73c05c8f3c1328607 +F src/util.c eae1eeaf2de968bc32d81274b6209a6685d8f68f8a44fb80bc77602860feede8 F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 @@ -775,7 +775,7 @@ F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7 F test/async4.test 1787e3952128aa10238bf39945126de7ca23685a F test/async5.test 383ab533fdb9f7ad228cc99ee66e1acb34cc0dc0 -F test/atof1.test b4dbc37187649e23ba92e06e0d2c076304516967b5a8b0af50f8e4f2323e654c +F test/atof1.test 191ce0d7b0d527aafeafe659c31e2433e430324b5ebce3fb066178b4d9035767 F test/atomic.test 065a453dde33c77ff586d91ccaa6ed419829d492dbb1a5694b8a09f3f9d7d061 F test/atomic2.test b6863b4aa552543874f80b42fb3063f1c8c2e3d8e56b6562f00a3cc347b5c1da F test/atrc.c c388fac43dbba05c804432a7135ae688b32e8f25818e9994ffba4b64cf60c27c @@ -2041,8 +2041,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 702243333843219f8904ee0fd12283080d6c6b3fc1ffb36c534cdefb3563c00d -R 0bc2956a2833c937de8e3462962e6784 +P 4fa6938dac2d3d813a37664053db31451a2a065f78dd212488f5f7f8d583ddc5 +R df6eb47114802d2c438e907f9bdbb126 U drh -Z 2f85ae3f7bbd29aeb71b560da07ddd48 +Z 7f5799444e9a2cb0ee3bb904fd4adcc7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8bb5c10510..a69b8691ba 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4fa6938dac2d3d813a37664053db31451a2a065f78dd212488f5f7f8d583ddc5 \ No newline at end of file +d3c48807100a358a70fdd799c8935eba1b765ace2e1ddea4475fd673006cb6da \ No newline at end of file diff --git a/src/util.c b/src/util.c index b7d6bd58e8..40662f021c 100644 --- a/src/util.c +++ b/src/util.c @@ -1027,7 +1027,7 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ mul2(r,rr, 1.0e-01, -5.5511151231257827021e-18, &r, &rr); } }else{ - while( r<1.0e-98 ){ + while( r<1.0e-82 ){ exp -= 100; mul2(r, rr, 1.0e+100, -1.5902891109759918046, &r, &rr); } diff --git a/test/atof1.test b/test/atof1.test index ecfbdd33ee..9386fb0589 100644 --- a/test/atof1.test +++ b/test/atof1.test @@ -51,7 +51,13 @@ for {set i 1} {$i<20000} {incr i} { set y [db eval {SELECT $x=CAST(quote($x) AS real)}] if {!$y} { db eval {SELECT real2hex($x) a, real2hex(CAST(quote($x) AS real)) b} {} - puts "\n!IN: $a $xf" + puts "" + if {$x<0} { + puts "[format {!SCALE: %17s 1 23456789 123456789 123456789} {}]" + } else { + puts "[format {!SCALE: %16s 1 23456789 123456789 123456789} {}]" + } + puts "!IN: $a $xf" puts [format {!QUOTE: %16s %s} {} [db eval {SELECT quote($x)}]] db eval {SELECT CAST(quote($x) AS real) c} {} puts "!OUT: $b [format %.32e $c]" From 5270d7424d2e9f47c450fe85753c0768e2f41b3b Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 3 Jul 2023 10:18:02 +0000 Subject: [PATCH 13/17] Faster Dekker multiplication that removes the restriction on input magnitude. FossilOrigin-Name: 2994caf5884be07c889519c78fbac4ddcf267fcfe6a3265ecb6390bcd574532e --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/util.c | 18 +++++++++--------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index 5ae086cfa2..593e1f7fd9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sone\sconstant\sin\sthe\snormalization\slogic.\s\sImproved\serror\soutput\nfrom\satof1.test. -D 2023-07-03T10:00:38.019 +C Faster\sDekker\smultiplication\sthat\sremoves\sthe\srestriction\son\sinput\smagnitude. +D 2023-07-03T10:18:02.499 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -705,7 +705,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c eae1eeaf2de968bc32d81274b6209a6685d8f68f8a44fb80bc77602860feede8 +F src/util.c e920f80d429285da398e33852e47b1731fd474d4fc8fd38ea16e74f7f15c100c F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 @@ -2041,8 +2041,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 4fa6938dac2d3d813a37664053db31451a2a065f78dd212488f5f7f8d583ddc5 -R df6eb47114802d2c438e907f9bdbb126 +P d3c48807100a358a70fdd799c8935eba1b765ace2e1ddea4475fd673006cb6da +R 46c57ca0802519e59042da4b712e623e U drh -Z 7f5799444e9a2cb0ee3bb904fd4adcc7 +Z 937d038b18f0b1ea37450ce437120a68 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a69b8691ba..1fa06bc7fa 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d3c48807100a358a70fdd799c8935eba1b765ace2e1ddea4475fd673006cb6da \ No newline at end of file +2994caf5884be07c889519c78fbac4ddcf267fcfe6a3265ecb6390bcd574532e \ No newline at end of file diff --git a/src/util.c b/src/util.c index 40662f021c..7456599bc9 100644 --- a/src/util.c +++ b/src/util.c @@ -940,10 +940,15 @@ static void mul2( double *z, double *zz ){ double hx, tx, hy, ty, p, q, c, cc; - p = 67108865.0 * x; - hx = x - p + p; tx = x - hx; - p = 67108865.0 * y; - hy = y - p + p; ty = y - hy; + u64 m; + memcpy(&m, &x, 8); + m &= 0xfffffffffc000000L; + memcpy(&hx, &m, 8); + tx = x - hx; + memcpy(&m, &y, 8); + m &= 0xfffffffffc000000L; + memcpy(&hy, &m, 8); + ty = y - hy; p = hx*hy; q = hx*ty + tx*hy; c = p+q; @@ -1010,11 +1015,6 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ double rr = 0.0; if( r>=1.0e+19 ){ while( r>=1.0e+119 ){ - if( r>=1.0e+300 ){ - exp += 300; - r *= 1.0e-300; - break; - } exp += 100; mul2(r, rr, 1.0e-100, -1.99918998026028836196e-117, &r, &rr); } From bc2b65832e97ec400a50116b525fde8fb1bd9580 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 3 Jul 2023 10:35:29 +0000 Subject: [PATCH 14/17] Squeeze as many bits as we can out of normalization. FossilOrigin-Name: 25d1ad6df1ed460c2cc9f32e8a5106e121ae502044f83abf431c1d5b3879eb56 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/util.c | 14 +++++++------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/manifest b/manifest index 593e1f7fd9..b7a2f9af88 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Faster\sDekker\smultiplication\sthat\sremoves\sthe\srestriction\son\sinput\smagnitude. -D 2023-07-03T10:18:02.499 +C Squeeze\sas\smany\sbits\sas\swe\scan\sout\sof\snormalization. +D 2023-07-03T10:35:29.478 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -705,7 +705,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c e920f80d429285da398e33852e47b1731fd474d4fc8fd38ea16e74f7f15c100c +F src/util.c 78be1d552e7038265bb944ef26c9ef94514bd061da19323a0321dde2874f0907 F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 @@ -2041,8 +2041,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d3c48807100a358a70fdd799c8935eba1b765ace2e1ddea4475fd673006cb6da -R 46c57ca0802519e59042da4b712e623e +P 2994caf5884be07c889519c78fbac4ddcf267fcfe6a3265ecb6390bcd574532e +R 50ea777199102a639989a65cb4bb5a5a U drh -Z 937d038b18f0b1ea37450ce437120a68 +Z 91cee9d1957f1c63a4a3d422340e91a7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1fa06bc7fa..a5a0d7abae 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2994caf5884be07c889519c78fbac4ddcf267fcfe6a3265ecb6390bcd574532e \ No newline at end of file +25d1ad6df1ed460c2cc9f32e8a5106e121ae502044f83abf431c1d5b3879eb56 \ No newline at end of file diff --git a/src/util.c b/src/util.c index 7456599bc9..6e740ef6ae 100644 --- a/src/util.c +++ b/src/util.c @@ -1013,29 +1013,29 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ v = (u64)rr; }else{ double rr = 0.0; - if( r>=1.0e+19 ){ - while( r>=1.0e+119 ){ + if( r>1.84e+19 ){ + while( r>1.84e+119 ){ exp += 100; mul2(r, rr, 1.0e-100, -1.99918998026028836196e-117, &r, &rr); } - while( r>=1.0e+29 ){ + while( r>1.84e+29 ){ exp += 10; mul2(r,rr, 1.0e-10, -3.6432197315497741579e-27, &r, &rr); } - while( r>=1.0e+19 ){ + while( r>1.84e+19 ){ exp += 1; mul2(r,rr, 1.0e-01, -5.5511151231257827021e-18, &r, &rr); } }else{ - while( r<1.0e-82 ){ + while( r<1.84e-82 ){ exp -= 100; mul2(r, rr, 1.0e+100, -1.5902891109759918046, &r, &rr); } - while( r<1.0e+08 ){ + while( r<1.84e+08 ){ exp -= 10; mul2(r, rr, 1.0e+10, 0.0, &r, &rr); } - while( r<1.0e+18 ){ + while( r<1.84e+18 ){ exp -= 1; mul2(r, rr, 1.0e+01, 0.0, &r, &rr); } From 841e596ad02b1b99e6c9578e9ecc5b0b56277948 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 3 Jul 2023 11:39:38 +0000 Subject: [PATCH 15/17] Final clean-up of the idea of using Dekker double-precision floating computations to improve accuracy. FossilOrigin-Name: 40285d631072b79aba39cc20d4b217bdf0069f29ac9902cb97dec28d3e4cde11 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/util.c | 15 ++++++++++++--- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index b7a2f9af88..7130835d4e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Squeeze\sas\smany\sbits\sas\swe\scan\sout\sof\snormalization. -D 2023-07-03T10:35:29.478 +C Final\sclean-up\sof\sthe\sidea\sof\susing\sDekker\sdouble-precision\sfloating\ncomputations\sto\simprove\saccuracy. +D 2023-07-03T11:39:38.542 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -705,7 +705,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c 78be1d552e7038265bb944ef26c9ef94514bd061da19323a0321dde2874f0907 +F src/util.c 44855f67ac96b546b77b6c2ad128d1e28a56c495038cdda3cae25b8407442f3c F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 @@ -2041,8 +2041,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 2994caf5884be07c889519c78fbac4ddcf267fcfe6a3265ecb6390bcd574532e -R 50ea777199102a639989a65cb4bb5a5a +P 25d1ad6df1ed460c2cc9f32e8a5106e121ae502044f83abf431c1d5b3879eb56 +R 8a559af6860b1623fc9d9f5fb9393b11 U drh -Z 91cee9d1957f1c63a4a3d422340e91a7 +Z 48672189b786297b4026c7966b4eccaa # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a5a0d7abae..7757e469cd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -25d1ad6df1ed460c2cc9f32e8a5106e121ae502044f83abf431c1d5b3879eb56 \ No newline at end of file +40285d631072b79aba39cc20d4b217bdf0069f29ac9902cb97dec28d3e4cde11 \ No newline at end of file diff --git a/src/util.c b/src/util.c index 6e740ef6ae..cf4745bd0b 100644 --- a/src/util.c +++ b/src/util.c @@ -999,8 +999,8 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ /* Multiply r by powers of ten until it lands somewhere in between ** 1.0e+19 and 1.0e+17. */ - if( sizeof(long double)>8 && 0 ){ - long double rr = r; + if( sizeof(LONGDOUBLE_TYPE)>8 ){ + LONGDOUBLE_TYPE 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; } @@ -1012,6 +1012,15 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ } v = (u64)rr; }else{ + /* If high-precision floating point is not available using "long double", + ** then use Dekker-style double-double computation to increase the + ** precision. + ** + ** The error terms on constants like 1.0e+100 computed using the + ** decimal extension, for example as follows: + ** + ** SELECT decimal_sci(decimal_sub('1.0e+100',decimal(1.0e+100))); + */ double rr = 0.0; if( r>1.84e+19 ){ while( r>1.84e+119 ){ @@ -1029,7 +1038,7 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ }else{ while( r<1.84e-82 ){ exp -= 100; - mul2(r, rr, 1.0e+100, -1.5902891109759918046, &r, &rr); + mul2(r, rr, 1.0e+100, -1.5902891109759918046e+83, &r, &rr); } while( r<1.84e+08 ){ exp -= 10; From 0478d1d88a523d3d8e5c8d5acd7eb9649937ec65 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 3 Jul 2023 12:58:18 +0000 Subject: [PATCH 16/17] New test cases for floating-point conversions. FossilOrigin-Name: 9435ed76cd9bb1be02768b3853a2323834f67bda75d3e3f76f6aa7bd0bade3d7 --- manifest | 16 ++++++------- manifest.uuid | 2 +- src/test1.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++ src/util.c | 14 +++++------ test/fpconv1.test | 44 ++++++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+), 16 deletions(-) create mode 100644 test/fpconv1.test diff --git a/manifest b/manifest index fd10ee4899..0c46f90368 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Use\sDekker\sdouble-precision\sfloating\spoint\sto\sdo\sthe\svalue\snormalization\nstep\sin\ssqlite3FpDecode()\sfor\ssystems\son\swhich\s"long\sdouble"\sis\s8\sbytes\sor\nless. -D 2023-07-03T11:44:15.056 +C New\stest\scases\sfor\sfloating-point\sconversions. +D 2023-07-03T12:58:18.851 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -647,7 +647,7 @@ F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 F src/tclsqlite.c ecbc3c99c0d0c3ed122a913f143026c26d38d57f33e06bb71185dd5c1efe37cd -F src/test1.c 64b8462099618a6243f63ba701eea8913046cd7377a7a77a7e3a0ada42219275 +F src/test1.c ac81daaee5135d210f926e7d1501d6065d396decac57af94083e677cf7e175bd F src/test2.c 827446e259a3b7ab949da1542953edda7b5117982576d3e6f1c24a0dd20a5cef F src/test3.c e5178558c41ff53236ae0271e9acb3d6885a94981d2eb939536ee6474598840e F src/test4.c 4533b76419e7feb41b40582554663ed3cd77aaa54e135cf76b3205098cd6e664 @@ -705,7 +705,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c 44855f67ac96b546b77b6c2ad128d1e28a56c495038cdda3cae25b8407442f3c +F src/util.c 97f9a7a1a22db23984ca3ced12ed666327c17beeaf8481c3325a0c74369060dc F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 @@ -1014,6 +1014,7 @@ F test/fkey8.test 51deda7f1a1448bca95875e4a6e1a3a75b4bd7215e924e845bd60de60e4d84 F test/fkey_malloc.test 594a7ea1fbab553c036c70813cd8bd9407d63749 F test/fordelete.test ba98f14446b310f9c9d935b97ec748753d0144a28b356ba30d1f4f6958fdde5c F test/format4.test eeae341953db8b6bda7f549044797c3278a6cc345d11ada81471671b654f8ef4 +F test/fpconv1.test d5d8aa0c427533006c112fb1957cdd1ea68c1d0709470dabb9ca02c2e4c06ad8 F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c F test/fts3.test 672a040ea57036fb4b6fdc09027c18d7d24ab654 F test/fts3_common.tcl dffad248f9ce090800e272017d2898005c28ee6314fc1dd5550643a02666907a @@ -2041,9 +2042,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P ce06982f880339cf46704e95c907249827c3e44af2b9420005200ca8abd3f371 40285d631072b79aba39cc20d4b217bdf0069f29ac9902cb97dec28d3e4cde11 -R 8a559af6860b1623fc9d9f5fb9393b11 -T +closed 40285d631072b79aba39cc20d4b217bdf0069f29ac9902cb97dec28d3e4cde11 +P 51f492b440728151ce64168d5436ba87ca60d84b89408e47c593fa731355afe4 +R 610c019b12f01dbb4053eb803e9a81bd U drh -Z 98f04f31ed955532d376706d5d138150 +Z 391291e9df52bc2766a8acd3f9c94d15 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b3071eda1b..f201e504af 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -51f492b440728151ce64168d5436ba87ca60d84b89408e47c593fa731355afe4 \ No newline at end of file +9435ed76cd9bb1be02768b3853a2323834f67bda75d3e3f76f6aa7bd0bade3d7 \ No newline at end of file diff --git a/src/test1.c b/src/test1.c index c77aff2ed2..013adcbcb0 100644 --- a/src/test1.c +++ b/src/test1.c @@ -991,6 +991,46 @@ static void intrealFunction( sqlite3_test_control(SQLITE_TESTCTRL_RESULT_INTREAL, context); } +/* +** SQL function: strtod(X) +** +** Use the C-library strtod() function to convert string X into a double. +** Used for comparing the accuracy of SQLite's internal text-to-float conversion +** routines against the C-library. +*/ +static void shellStrtod( + sqlite3_context *pCtx, + int nVal, + sqlite3_value **apVal +){ + char *z = (char*)sqlite3_value_text(apVal[0]); + UNUSED_PARAMETER(nVal); + if( z==0 ) return; + sqlite3_result_double(pCtx, strtod(z,0)); +} + +/* +** SQL function: dtostr(X) +** +** Use the C-library printf() function to convert real value X into a string. +** Used for comparing the accuracy of SQLite's internal float-to-text conversion +** routines against the C-library. +*/ +static void shellDtostr( + sqlite3_context *pCtx, + int nVal, + sqlite3_value **apVal +){ + double r = sqlite3_value_double(apVal[0]); + int n = nVal>=2 ? sqlite3_value_int(apVal[1]) : 26; + char z[400]; + if( n<1 ) n = 1; + if( n>350 ) n = 350; + sprintf(z, "%#+.*e", n, r); + sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT); +} + + /* ** Usage: sqlite3_create_function DB ** @@ -1063,6 +1103,27 @@ static int SQLITE_TCLAPI test_create_function( 0, intrealFunction, 0, 0); } + /* Functions strtod() and dtostr() work as in the shell. These routines + ** use the standard C library to convert between floating point and + ** text. This is used to compare SQLite's internal conversion routines + ** against the standard library conversion routines. + ** + ** Both routines copy/pasted from the shell.c.in implementation + ** on 2023-07-03. + */ + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function(db, "strtod", 1, SQLITE_UTF8, 0, + shellStrtod, 0, 0); + } + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function(db, "dtostr", 1, SQLITE_UTF8, 0, + shellDtostr, 0, 0); + } + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function(db, "dtostr", 2, SQLITE_UTF8, 0, + shellDtostr, 0, 0); + } + #ifndef SQLITE_OMIT_UTF16 /* Use the sqlite3_create_function16() API here. Mainly for fun, but also ** because it is not tested anywhere else. */ diff --git a/src/util.c b/src/util.c index cf4745bd0b..7642a29bca 100644 --- a/src/util.c +++ b/src/util.c @@ -934,7 +934,7 @@ int sqlite3Atoi(const char *z){ ** T. J. Dekker, "A Floating-Point Technique for Extending the ** Available Precision". 1971-07-26. */ -static void mul2( +static void dekkerMul2( double x, double xx, double y, double yy, double *z, double *zz @@ -1025,28 +1025,28 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ if( r>1.84e+19 ){ while( r>1.84e+119 ){ exp += 100; - mul2(r, rr, 1.0e-100, -1.99918998026028836196e-117, &r, &rr); + dekkerMul2(r, rr, 1.0e-100, -1.99918998026028836196e-117, &r, &rr); } while( r>1.84e+29 ){ exp += 10; - mul2(r,rr, 1.0e-10, -3.6432197315497741579e-27, &r, &rr); + dekkerMul2(r,rr, 1.0e-10, -3.6432197315497741579e-27, &r, &rr); } while( r>1.84e+19 ){ exp += 1; - mul2(r,rr, 1.0e-01, -5.5511151231257827021e-18, &r, &rr); + dekkerMul2(r,rr, 1.0e-01, -5.5511151231257827021e-18, &r, &rr); } }else{ while( r<1.84e-82 ){ exp -= 100; - mul2(r, rr, 1.0e+100, -1.5902891109759918046e+83, &r, &rr); + dekkerMul2(r, rr, 1.0e+100, -1.5902891109759918046e+83, &r, &rr); } while( r<1.84e+08 ){ exp -= 10; - mul2(r, rr, 1.0e+10, 0.0, &r, &rr); + dekkerMul2(r, rr, 1.0e+10, 0.0, &r, &rr); } while( r<1.84e+18 ){ exp -= 1; - mul2(r, rr, 1.0e+01, 0.0, &r, &rr); + dekkerMul2(r, rr, 1.0e+01, 0.0, &r, &rr); } } v = (u64)(r)+(u64)(rr); diff --git a/test/fpconv1.test b/test/fpconv1.test new file mode 100644 index 0000000000..195fdf9904 --- /dev/null +++ b/test/fpconv1.test @@ -0,0 +1,44 @@ +# 2023-07-03 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file contains a test that attempts to verify the claim that the +# floatpoint-to-text conversion routines built into SQLite maintain at +# least 15 significant digits of accuracy. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +if {[catch {load_static_extension db decimal} error]} { + puts "Skipping decimal tests, hit load error: $error" + finish_test; return +} + +sqlite3_create_function db +do_execsql_test fpconv1-1.0 { + WITH RECURSIVE + /* Number of random floating-point values to try. + ** On a circa 2016 x64 linux box, this test runs at + ** about 80000 cases per second -------------------vvvvvv */ + c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100000), + fp(y) AS MATERIALIZED ( + SELECT CAST( format('%+d.%019d0e%+03d', + random()%10,abs(random()),random()%200) AS real) + FROM c + ) + SELECT y FROM fp + WHERE -log10(abs(decimal_sub(dtostr(y,24),format('%!.24e',y))/y))<15.0; + /* Number of digits of accuracy required -------^^^^ */ +} {} +# ^---- Expect a empty set as the result. The output is all tested numbers +# that fail to preserve at least 15 significant digits of accuracy. + +finish_test From 1b3c6e727e11417a687fc09193828f5142a1b195 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 3 Jul 2023 14:56:49 +0000 Subject: [PATCH 17/17] Whether or not to use the long-double floating point conversion technique is now a test-control setting. FossilOrigin-Name: 02ae6caff1f7925c696ad263fe78b1036364d24b6373e1baec10d53aafb14a12 --- manifest | 24 ++++++++++++------------ manifest.uuid | 2 +- src/global.c | 1 + src/main.c | 12 ++++++++++++ src/shell.c.in | 9 +++++++++ src/sqlite.h.in | 3 ++- src/sqliteInt.h | 1 + src/test1.c | 25 +++++++++++++++++++++++++ src/util.c | 6 +++--- 9 files changed, 66 insertions(+), 17 deletions(-) diff --git a/manifest b/manifest index 0c46f90368..7cabc9b86f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C New\stest\scases\sfor\sfloating-point\sconversions. -D 2023-07-03T12:58:18.851 +C Whether\sor\snot\sto\suse\sthe\slong-double\sfloating\spoint\sconversion\stechnique\nis\snow\sa\stest-control\ssetting. +D 2023-07-03T14:56:49.741 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -591,7 +591,7 @@ F src/expr.c 8d1656b65e26af3e34f78e947ac423f0d20c214ed25a67486e433bf16ca6b543 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c a7fcbf7e66d14dbb73cf49f31489ebf66d0e6006c62b95246924a3bae9f37b36 F src/func.c 6028c160f693bdd018b651b5468a0a8e790f4e01e200796916b2d10a5d3237aa -F src/global.c bd0892ade7289f6e20bff44c07d06371f2ff9b53cea359e7854b9b72f65adc30 +F src/global.c a16553245e315ee0cda8f9b0bf744efef9dc99f86e9d77f58975ea58824ded92 F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220 F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 @@ -600,7 +600,7 @@ F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 F src/json.c 14c474fb1249a46eb44e878e2361f36abfe686b134039b0d1883d93d61505b4a F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 176d6b2cb18a6ad73b133db17f6fc351c4d9a2d510deebdb76c22bde9cfd1465 -F src/main.c 5fd4b65d61ae6155f36756ed508a39b38b49355b031188961e8d923f43f4bc49 +F src/main.c 7bc04e53851c813454b73083e21f84fd8a6d3d32eaa242bb4974eda870e28fef F src/malloc.c 47b82c5daad557d9b963e3873e99c22570fb470719082c6658bf64e3012f7d23 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2 @@ -638,16 +638,16 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 3ab1186290a311a8ceed1286c0e286209f7fe97b2d02c7593258004ce295dd88 -F src/shell.c.in 2c02c819349de410d63fcc0217763dfe5a42dbe58f2d68046d4ea8a376d12c26 -F src/sqlite.h.in 3076d78836b6dac53b3ab0875fc8fd15bca8077aad4d33c85336e05af6aef8c7 +F src/shell.c.in e01d7e1d8a6b158320c97c955e1b14763289b82a73e3a155fa8efe0d546f0567 +F src/sqlite.h.in f999ef3642f381d69679b2516b430dbcb6c5a2a951b7f5e43dc4751b474a5774 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4 -F src/sqliteInt.h 3d4125cc1c38777f307e7d374890f085d09f6e15298e09a4f74919744ec2ad62 +F src/sqliteInt.h f6c5470b7db42318a3de1115757e94b76570ad7697ac547823e01f1166756f1d F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 F src/tclsqlite.c ecbc3c99c0d0c3ed122a913f143026c26d38d57f33e06bb71185dd5c1efe37cd -F src/test1.c ac81daaee5135d210f926e7d1501d6065d396decac57af94083e677cf7e175bd +F src/test1.c 86099cc02f22069d75b04ae9c105acde0edbce9033d5ac37262bad2e461e6000 F src/test2.c 827446e259a3b7ab949da1542953edda7b5117982576d3e6f1c24a0dd20a5cef F src/test3.c e5178558c41ff53236ae0271e9acb3d6885a94981d2eb939536ee6474598840e F src/test4.c 4533b76419e7feb41b40582554663ed3cd77aaa54e135cf76b3205098cd6e664 @@ -705,7 +705,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731 F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c 97f9a7a1a22db23984ca3ced12ed666327c17beeaf8481c3325a0c74369060dc +F src/util.c 452f1e5e4a85b7869daf15d9b6468ac754836467004fa04c3419f2bd628eb6ad F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 @@ -2042,8 +2042,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 51f492b440728151ce64168d5436ba87ca60d84b89408e47c593fa731355afe4 -R 610c019b12f01dbb4053eb803e9a81bd +P 9435ed76cd9bb1be02768b3853a2323834f67bda75d3e3f76f6aa7bd0bade3d7 +R c19a4cd19e732c53bd925a8fc2b0f976 U drh -Z 391291e9df52bc2766a8acd3f9c94d15 +Z ccc178a785227b10fda86cb46a381790 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f201e504af..64fd4d3539 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9435ed76cd9bb1be02768b3853a2323834f67bda75d3e3f76f6aa7bd0bade3d7 \ No newline at end of file +02ae6caff1f7925c696ad263fe78b1036364d24b6373e1baec10d53aafb14a12 \ No newline at end of file diff --git a/src/global.c b/src/global.c index fcba7d7fa2..dc765522ec 100644 --- a/src/global.c +++ b/src/global.c @@ -243,6 +243,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = { SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ 0, /* bSmallMalloc */ 1, /* bExtraSchemaChecks */ + sizeof(long double)>8, /* bUseLongDouble */ 0x7ffffffe, /* mxStrlen */ 0, /* neverCorrupt */ SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */ diff --git a/src/main.c b/src/main.c index 7e3fab8861..19f65dac6f 100644 --- a/src/main.c +++ b/src/main.c @@ -4470,6 +4470,18 @@ int sqlite3_test_control(int op, ...){ break; } + /* sqlite3_test_control(SQLITE_TESTCTRL_USELONGDOUBLE, int X); + ** + ** Enable long double usage if X>0. Disable if X==0. No-op if X<0. + ** Return the status of long double usage afterwards. + */ + case SQLITE_TESTCTRL_USELONGDOUBLE: { + int b = va_arg(ap, int); + if( b>=0 ) sqlite3Config.bUseLongDouble = b>0; + rc = sqlite3Config.bUseLongDouble!=0; + break; + } + #if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD) /* sqlite3_test_control(SQLITE_TESTCTRL_TUNE, id, *piValue) diff --git a/src/shell.c.in b/src/shell.c.in index 2b0884e1c0..295d405cbd 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -10876,6 +10876,7 @@ static int do_meta_command(char *zLine, ShellState *p){ {"seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" }, {"sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" }, {"tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" }, + {"uselongdouble", SQLITE_TESTCTRL_USELONGDOUBLE,0,"BOOLEAN" }, }; int testctrl = -1; int iCtrl = -1; @@ -10997,6 +10998,14 @@ static int do_meta_command(char *zLine, ShellState *p){ } break; + /* sqlite3_test_control(int, int) */ + case SQLITE_TESTCTRL_USELONGDOUBLE: { + int opt = nArg==3 ? booleanValue(azArg[2]) : -1; + rc2 = sqlite3_test_control(testctrl, opt); + isOk = 1; + break; + } + /* sqlite3_test_control(sqlite3*) */ case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: rc2 = sqlite3_test_control(testctrl, p->db); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index c5a50c01be..59d9986c52 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -8176,7 +8176,8 @@ int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_TRACEFLAGS 31 #define SQLITE_TESTCTRL_TUNE 32 #define SQLITE_TESTCTRL_LOGEST 33 -#define SQLITE_TESTCTRL_LAST 33 /* Largest TESTCTRL */ +#define SQLITE_TESTCTRL_USELONGDOUBLE 34 +#define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ /* ** CAPI3REF: SQL Keyword Checking diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 5662ab4fd2..8eae8d166a 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4090,6 +4090,7 @@ struct Sqlite3Config { u8 bUseCis; /* Use covering indices for full-scans */ u8 bSmallMalloc; /* Avoid large memory allocations if true */ u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */ + u8 bUseLongDouble; /* Make use of long double */ int mxStrlen; /* Maximum string length */ int neverCorrupt; /* Database is always well-formed */ int szLookaside; /* Default lookaside buffer size */ diff --git a/src/test1.c b/src/test1.c index 013adcbcb0..acae44c8ac 100644 --- a/src/test1.c +++ b/src/test1.c @@ -7098,6 +7098,30 @@ static int SQLITE_TCLAPI extra_schema_checks( return TCL_OK; } +/* +** tclcmd: use_long_double INT +** +** Enable or disable the use of long double. Enable if the argument is +** positive. Disable if the argument is zero. No-op if the argument is +** negative. +** +** Return the new setting. +*/ +static int SQLITE_TCLAPI use_long_double( + ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + int i = -1; + if( objc==2 ){ + if( Tcl_GetBooleanFromObj(interp,objv[1],&i) ) return TCL_ERROR; + } + i = sqlite3_test_control(SQLITE_TESTCTRL_USELONGDOUBLE, i); + Tcl_SetObjResult(interp, Tcl_NewIntObj(i)); + return TCL_OK; +} + /* ** tclcmd: database_may_be_corrupt ** @@ -8986,6 +9010,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "reset_prng_state", reset_prng_state, 0 }, { "prng_seed", prng_seed, 0 }, { "extra_schema_checks", extra_schema_checks, 0}, + { "use_long_double", use_long_double, 0}, { "database_never_corrupt", database_never_corrupt, 0}, { "database_may_be_corrupt", database_may_be_corrupt, 0}, { "optimization_control", optimization_control,0}, diff --git a/src/util.c b/src/util.c index 7642a29bca..dbf31d30f3 100644 --- a/src/util.c +++ b/src/util.c @@ -999,7 +999,7 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ /* Multiply r by powers of ten until it lands somewhere in between ** 1.0e+19 and 1.0e+17. */ - if( sizeof(LONGDOUBLE_TYPE)>8 ){ + if( sqlite3Config.bUseLongDouble ){ LONGDOUBLE_TYPE rr = r; if( rr>=1.0e+19 ){ while( rr>=1.0e+119L ){ exp+=100; rr *= 1.0e-100L; } @@ -1049,7 +1049,7 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ dekkerMul2(r, rr, 1.0e+01, 0.0, &r, &rr); } } - v = (u64)(r)+(u64)(rr); + v = rr<0.0 ? (u64)r-(u64)(-rr) : (u64)r+(u64)rr; } @@ -1089,7 +1089,7 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ } } memmove(p->z, &p->z[i+1], p->n); - while( p->n>0 && p->z[p->n-1]=='0' ){ p->n--; } + while( ALWAYS(p->n>0) && p->z[p->n-1]=='0' ){ p->n--; } } /*