From 3a42db1ad7f95e2e161e36f471cf524bfa4c4335 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 10 Jun 2024 18:10:35 +0000 Subject: [PATCH] More aggressive rounding behavior for the round() function only. Format() still uses the classic behavior, and the same behavior exhibited by printf() in glibc. FossilOrigin-Name: a1b57288d7076cb9e26ac429f01e9264240d6af26243a195b2065cf667bb8bb6 --- manifest | 19 ++++++++----------- manifest.uuid | 2 +- src/func.c | 2 +- src/printf.c | 6 +++++- src/util.c | 30 ++++++++++++++++++++---------- 5 files changed, 35 insertions(+), 24 deletions(-) diff --git a/manifest b/manifest index 8d40a46dd4..a1414b8c6d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Change\sthe\srounding\sbehavior\sof\sfloat\spoint\sto\sdecimal\sconversions\ssuch\sthat\nif\sthe\snext\sdigit\sis\s4\sbut\sthe\svalue\sis\swithin\sone\sepsilon\sof\sthe\snext\sdigit\nbeing\s5\sand\sif\sthe\sepsilon\sis\ssmall\scompared\sthe\snumber\sof\sdigits\sto\sbe\nrendered,\sthen\sgo\sahead\sand\sround\sup\sanyhow,\neven\sthough\sthe\scorrect\sbehavior\swould\sbe\sto\sround\sdown. -D 2024-06-10T14:31:07.017 +C More\saggressive\srounding\sbehavior\sfor\sthe\sround()\sfunction\sonly.\nFormat()\sstill\suses\sthe\sclassic\sbehavior,\sand\sthe\ssame\sbehavior\sexhibited\nby\sprintf()\sin\sglibc. +D 2024-06-10T18:10:35.427 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -708,7 +708,7 @@ F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500 F src/expr.c af9c9242be0df17280faf36c9810339de9df3d7a64ac8d33a5190a1400086ee5 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c 852f93c0ef995e0c2b8983059a2b97151c194cc8259e21f5bc2b7ac508348c2a -F src/func.c 1f61e32e7a357e615b5d2e774bee563761fce4f2fd97ecb0f72c33e62a2ada5f +F src/func.c 42053720bb3bf4e4e06072f22c2cde4a0477961990a31c4887f3668f8cf4543b F src/global.c 61a419dd9e993b9be0f91de4c4ccf322b053eb829868e089f0321dd669be3b90 F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220 F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 @@ -751,7 +751,7 @@ F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 F src/pragma.c 52bfbf6dfd668b69b5eb9bd1186e3a67367c8453807150d6e75239229924f684 F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c d99931f45416652895e502328ca49fe782cfc4e1ebdcda13b3736d991ebf42ce -F src/printf.c 8b250972305e14b365561be5117ed0fd364e4fd58968776df1ce64c6280b90f9 +F src/printf.c bdb1b615431486709f4a52149468b509a6f04ef5535716e3d05a12c7ddac6de0 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 7e8d23ce7cdbfedf351a47e759f2722e8182ca10fd7580be43f4ce1f1a228145 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 @@ -823,7 +823,7 @@ F src/trigger.c 0858f75818ed1580332db274f1032bcc5effe567cb132df5c5be8b1d800ca97f F src/update.c 732404a04d1737ef14bb6ec6b84f74edf28b3c102a92ae46b4855438a710efe7 F src/upsert.c 2e60567a0e9e8520c18671b30712a88dc73534474304af94f32bb5f3ef65ac65 F src/utf.c f23165685a67b4caf8ec08fb274cb3f319103decfb2a980b7cfd55d18dfa855e -F src/util.c cedda44359c51d971557614216085c31d78ee7d23cc89400061c5433855aff64 +F src/util.c 6bd44646223215079588dcac46206c3200d5ba29b9866fabb94dc5a98973f971 F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c b05777c3ff2ed7b9dfc347e7cdee18e371aa6811cef1fe83454691b0dbe2cc9f F src/vdbe.h c2d78d15112c3fc5ab87f5e8e0b75d2db1c624409de2e858c3d1aafb1650bb4f @@ -2196,11 +2196,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 56af06fa12104a1fe119d7087746011183af053834eac72d0fb69f60d98054c6 -R d2547c87a8223548d3daae3e983f63e3 -T *branch * round-up -T *sym-round-up * -T -sym-trunk * +P 4a790d3b28685f08bbb722057cd6a97aea08a2b2a6098562c6373fd3b5b7206c +R 73295dcd1493f41c7a4fa684616e652f U drh -Z fe023add6bc94c6c74f3cca88a0e7276 +Z 16d69236535ed95782490792d241c4e4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 79ca38fb04..41e392c7aa 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4a790d3b28685f08bbb722057cd6a97aea08a2b2a6098562c6373fd3b5b7206c \ No newline at end of file +a1b57288d7076cb9e26ac429f01e9264240d6af26243a195b2065cf667bb8bb6 \ No newline at end of file diff --git a/src/func.c b/src/func.c index 8fcda11dc0..ec1228bb75 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 c0dcc5d0fa..9640b0cac8 100644 --- a/src/printf.c +++ b/src/printf.c @@ -503,7 +503,11 @@ void sqlite3_str_vappendf( }else{ iRound = precision+1; } - sqlite3FpDecode(&s, realvalue, iRound, flag_altform2 ? 26 : 16); + sqlite3FpDecode(&s, realvalue, iRound, + flag_altform2 ? 26+flag_alternateform : 16); + /* ^^^^^^^^^^^^^^^^^^^--- Undocumented behavior: + ** When both '#' and '!' flags are present, the rounding behavior + ** is changed. See "rule 3" in the sqlite3FpDecode docs. */ if( s.isSpecial ){ if( s.isSpecial==2 ){ bufpt = flag_zeropad ? "null" : "NaN"; diff --git a/src/util.c b/src/util.c index fb25fa4b29..e4c8159a3f 100644 --- a/src/util.c +++ b/src/util.c @@ -1012,15 +1012,20 @@ int sqlite3Atoi(const char *z){ } /* -** Return true if the first N characters of string z[] are '9' +** z[] is the complete list of digits for a floating point conversion. +** The z[iRound] character is a 4. This routine checks to see if the +** iRound-1 character should be rounded up even though z[iRound] is not +** a 5. +** +** Return true if the 4 is followed by at least three 9s and all digits +** past the 4 are 9s out to the limit of precision. */ -static SQLITE_NOINLINE int allNines(const char *z, int N){ +static SQLITE_NOINLINE int shouldRoundUp(const char *z, int n, int iRound){ int i; - assert( N>0 ); - for(i=0; i15; } /* @@ -1053,7 +1058,13 @@ static SQLITE_NOINLINE int allNines(const char *z, int N){ ** Rule (3) is so that things like round(0.15,1) will come out as 0.2 ** even though the stored value for 0.15 is really ** 0.1499999999999999944488848768742172978818416595458984375 and ought -** to round down to 0.1. +** to round down to 0.1. Rule (3) is only applied if mxRound==27. +** +** This routine is normally only called from printf()/format(). In that +** case, mxRound is usually 16 but is increased to 26 with the "!" flag. +** Undocumented behavior: mxRound is 27 with the "#" and "!" flags. The +** round() function uses this undocumented flag combination to activate +** rounding rule (3). */ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ int i; @@ -1168,8 +1179,7 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ char *z = &p->zBuf[i+1]; if( iRound>mxRound ) iRound = mxRound; if( z[iRound]>='5' - || (z[iRound]=='4' && p->n>iRound+5 - && allNines(&z[iRound+1],p->n-iRound-3)) + || (z[iRound]=='4' && mxRound>=27 && shouldRoundUp(z, p->n, iRound)) ){ int j = iRound-1; while( 1 /*exit-by-break*/ ){ -- 2.47.2