Improved precision of binary-to-decimal conversion of floating-point values

on all systems but especially non-x86 systems that lake extended precision
hardward floating point.

FossilOrigin-Name: 32befb224b254639e756fd493f300507ff60c5222e00a7338dbcd39140133964
This commit is contained in:
drh 2023-07-03 15:03:59 +00:00
commit c9f240cea4
14 changed files with 448 additions and 176 deletions

View File

@ -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 Improved\sprecision\sof\sbinary-to-decimal\sconversion\sof\sfloating-point\svalues\non\sall\ssystems\sbut\sespecially\snon-x86\ssystems\sthat\slake\sextended\sprecision\nhardward\sfloating\spoint.
D 2023-07-03T15:03:59.842
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -590,8 +590,8 @@ 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/global.c bd0892ade7289f6e20bff44c07d06371f2ff9b53cea359e7854b9b72f65adc30
F src/func.c 6028c160f693bdd018b651b5468a0a8e790f4e01e200796916b2d10a5d3237aa
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
@ -633,21 +633,21 @@ 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 84b7b4b647f336934a5ab2e7f0c52555833cc0778d2d60e016cca52ee8c6cd8f
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 b677ba33397479f9a2fa1223fcd24b561cc8c77c7d073798f7b7118e15d69dbb
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 64b8462099618a6243f63ba701eea8913046cd7377a7a77a7e3a0ada42219275
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 4264102045fdb36e9af3ff361e390a5f7a76342a2bd7069e55d8ad332026d6b5
F src/util.c 452f1e5e4a85b7869daf15d9b6468ac754836467004fa04c3419f2bd628eb6ad
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 191ce0d7b0d527aafeafe659c31e2433e430324b5ebce3fb066178b4d9035767
F test/atomic.test 065a453dde33c77ff586d91ccaa6ed419829d492dbb1a5694b8a09f3f9d7d061
F test/atomic2.test b6863b4aa552543874f80b42fb3063f1c8c2e3d8e56b6562f00a3cc347b5c1da
F test/atrc.c c388fac43dbba05c804432a7135ae688b32e8f25818e9994ffba4b64cf60c27c
@ -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
@ -1399,7 +1400,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,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 f936f101406069b29218c89a36581b4497226fb61906782ea368f12d943c901c 4e8718dc35dbbaf75f17265a88d14acd9750dc75efbadf41377f9c97e732009c
R 4da01a7efae23c104b98c2eba70ec2ef
T +closed 4e8718dc35dbbaf75f17265a88d14acd9750dc75efbadf41377f9c97e732009c
U dan
Z 8ca5997f8884b68f398b8d0f077d59ae
P 7afad1f759f7ceda873c6d869422fd56fe4399c2d24d47ad9bc3b84b06d830d1 02ae6caff1f7925c696ad263fe78b1036364d24b6373e1baec10d53aafb14a12
R c19a4cd19e732c53bd925a8fc2b0f976
U drh
Z d52b1cd0e5057b695ef6926f80a9c961
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
7afad1f759f7ceda873c6d869422fd56fe4399c2d24d47ad9bc3b84b06d830d1
32befb224b254639e756fd493f300507ff60c5222e00a7338dbcd39140133964

View File

@ -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;
@ -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 ),

View File

@ -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 */

View File

@ -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)

View File

@ -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,15 @@ 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 */
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 +479,61 @@ void sqlite3_str_vappendf(
break;
case etFLOAT:
case etEXP:
case etGENERIC:
case etGENERIC: {
FpDecode s;
int iRound;
int j;
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;
if( xtype==etFLOAT ){
iRound = -precision;
}else if( xtype==etGENERIC ){
iRound = precision;
}else{
iRound = precision+1;
}
sqlite3FpDecode(&s, realvalue, iRound, flag_altform2 ? 26 : 16);
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{
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;
}
}
if( s.sign=='-' ){
prefix = '-';
}else{
prefix = flag_prefix;
}
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 +553,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 +572,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++) = j<s.n ? s.z[j++] : '0';
if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ',';
}
}
@ -681,19 +587,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++) = j<s.n ? s.z[j++] : '0';
}
/* Remove trailing zeros and the "." if no digits follow the "." */
if( flag_rtz && flag_dp ){
@ -709,6 +608,7 @@ void sqlite3_str_vappendf(
}
/* Add the "eNNN" suffix */
if( xtype==etEXP ){
exp = s.iDP - 1;
*(bufpt++) = aDigits[infop->charset];
if( exp<0 ){
*(bufpt++) = '-'; exp = -exp;
@ -742,8 +642,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;

View File

@ -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);

View File

@ -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

View File

@ -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;
@ -4089,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 */
@ -4597,6 +4599,19 @@ 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 isSpecial; /* 1: Infinity 2: NaN */
int n; /* Significant digits in the decode */
int iDP; /* Location of the decimal point */
char z[24]; /* Significiant digits */
};
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)

View File

@ -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. */
@ -7037,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
**
@ -8925,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},

View File

@ -928,6 +928,170 @@ 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 dekkerMul2(
double x, double xx,
double y, double yy,
double *z, double *zz
){
double hx, tx, hy, ty, p, q, c, cc;
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;
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.
**
** 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, int iRound, int mxRound){
int i;
u64 v;
int e, exp = 0;
p->isSpecial = 0;
/* Convert negative numbers to positive. Deal with Infinity, 0.0, and
** NaN. */
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)==0x7ff ){
p->isSpecial = 1 + (v!=0x7ff0000000000000L);
p->n = 0;
p->iDP = 0;
return;
}
/* Multiply r by powers of ten until it lands somewhere in between
** 1.0e+19 and 1.0e+17.
*/
if( sqlite3Config.bUseLongDouble ){
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; }
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{
/* 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 ){
exp += 100;
dekkerMul2(r, rr, 1.0e-100, -1.99918998026028836196e-117, &r, &rr);
}
while( r>1.84e+29 ){
exp += 10;
dekkerMul2(r,rr, 1.0e-10, -3.6432197315497741579e-27, &r, &rr);
}
while( r>1.84e+19 ){
exp += 1;
dekkerMul2(r,rr, 1.0e-01, -5.5511151231257827021e-18, &r, &rr);
}
}else{
while( r<1.84e-82 ){
exp -= 100;
dekkerMul2(r, rr, 1.0e+100, -1.5902891109759918046e+83, &r, &rr);
}
while( r<1.84e+08 ){
exp -= 10;
dekkerMul2(r, rr, 1.0e+10, 0.0, &r, &rr);
}
while( r<1.84e+18 ){
exp -= 1;
dekkerMul2(r, rr, 1.0e+01, 0.0, &r, &rr);
}
}
v = rr<0.0 ? (u64)r-(u64)(-rr) : (u64)r+(u64)rr;
}
/* 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;
p->iDP = p->n + exp;
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 && (iRound<p->n || 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;
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( ALWAYS(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.

View File

@ -51,10 +51,16 @@ 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 ""
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]"
puts "!OUT: $b [format %.32e $c]"
}
set y
} {1}

44
test/fpconv1.test Normal file
View File

@ -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

View File

@ -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
}