Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Change the rounding behavior of float point to decimal conversions such that if the next digit is 4 but the value is within one epsilon of the next digit being 5 and if the epsilon is small compared the number of digits to be rendered, then go ahead and round up anyhow, even though the correct behavior would be to round down. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | round-up |
Files: | files | file ages | folders |
SHA3-256: |
4a790d3b28685f08bbb722057cd6a97a |
User & Date: | drh 2024-06-10 14:31:07 |
Context
2024-06-12
| ||
00:30 | Test cases for the round() function for values within one epsilon of the 5 round-up threshold. (check-in: 552b1b10 user: drh tags: round-up-2) | |
2024-06-10
| ||
18:10 | More aggressive rounding behavior for the round() function only. Format() still uses the classic behavior, and the same behavior exhibited by printf() in glibc. (check-in: a1b57288 user: drh tags: round-up) | |
14:31 | Change the rounding behavior of float point to decimal conversions such that if the next digit is 4 but the value is within one epsilon of the next digit being 5 and if the epsilon is small compared the number of digits to be rendered, then go ahead and round up anyhow, even though the correct behavior would be to round down. (check-in: 4a790d3b user: drh tags: round-up) | |
12:43 | Improved header comment on the sqlite3FpDecode() implementation. For the fpdecode() SQL function (available in debug builds only) limit the value of the third parameter (mxRound) to be positive. (check-in: 56af06fa user: drh tags: trunk) | |
Changes
Changes to src/util.c.
︙ | ︙ | |||
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 | ** string is not an integer, just return 0. */ int sqlite3Atoi(const char *z){ int x = 0; sqlite3GetInt32(z, &x); return x; } /* ** Decode a floating-point value into an approximate decimal ** representation. ** ** If iRound<=0 then round to -iRound significant digits to the ** the left of the decimal point, or to a maximum of mxRound total ** significant digits. ** ** If iRound>0 round to min(iRound,mxRound) significant digits total. ** ** mxRound must be positive. ** ** The significant digits of the decimal representation are ** stored in p->z[] which is a often (but not always) a pointer ** into the middle of p->zBuf[]. There are p->n significant digits. ** The p->z[] array is *not* zero-terminated. */ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ int i; u64 v; int e, exp = 0; p->isSpecial = 0; p->z = p->zBuf; | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 | ** string is not an integer, just return 0. */ int sqlite3Atoi(const char *z){ int x = 0; sqlite3GetInt32(z, &x); return x; } /* ** Return true if the first N characters of string z[] are '9' */ static SQLITE_NOINLINE int allNines(const char *z, int N){ int i; assert( N>0 ); for(i=0; i<N; i++){ if( z[i]!='9' ) return 0; } return 1; } /* ** Decode a floating-point value into an approximate decimal ** representation. ** ** If iRound<=0 then round to -iRound significant digits to the ** the left of the decimal point, or to a maximum of mxRound total ** significant digits. ** ** If iRound>0 round to min(iRound,mxRound) significant digits total. ** ** mxRound must be positive. ** ** The significant digits of the decimal representation are ** stored in p->z[] which is a often (but not always) a pointer ** into the middle of p->zBuf[]. There are p->n significant digits. ** The p->z[] array is *not* zero-terminated. ** ** Rounding Behavior: ** ** (1) If the next digit is 3 or less, then truncate. Do not round. ** ** (2) If the next digit is 5 or more, then round up. ** ** (3) Round up if the next digit is a 4 followed by three or ** more 9 digits and all digits after the 4 up to the ** antipenultimate digit are 9. Otherwise truncate. ** ** 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. */ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ int i; u64 v; int e, exp = 0; p->isSpecial = 0; p->z = p->zBuf; |
︙ | ︙ | |||
1136 1137 1138 1139 1140 1141 1142 | p->n++; p->iDP++; } } if( iRound>0 && (iRound<p->n || p->n>mxRound) ){ char *z = &p->zBuf[i+1]; if( iRound>mxRound ) iRound = mxRound; | < | > > > | > | | 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 | p->n++; p->iDP++; } } if( iRound>0 && (iRound<p->n || p->n>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)) ){ 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'; iRound++; p->iDP++; break; }else{ j--; } } } p->n = iRound; } p->z = &p->zBuf[i+1]; assert( i+p->n < sizeof(p->zBuf) ); 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 |
︙ | ︙ |
Added test/round2.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | # 2024-06-10 # # 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. # #*********************************************************************** # # https://sqlite.org/forum/forumpost/c0753dfb2d5d7f75 # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix round2 load_static_extension db stmtrand do_execsql_test 1.1 { WITH RECURSIVE c(n) AS (VALUES(0) UNION ALL SELECT n+1 FROM c WHERE n<100000), r(a,b) AS MATERIALIZED (SELECT stmtrand()%100000, stmtrand()%100000 FROM c), f(x,y,n) AS ( SELECT CAST(format('%d.%d5',a,b) AS real), CAST(format('%d.%d6',a,b) AS real), length(format('%d',b)) FROM r) SELECT x, n, round(x,n), round(y,n) FROM f WHERE round(x,n)<>round(y,n); } {} do_execsql_test 1.2 { SELECT round(0.15,1); } 0.2 do_execsql_test 1.3 { SELECT round(0.14999999999999999,1); } 0.2 do_execsql_test 1.4 { SELECT round(0.1499999999999999944488848768742172978818416595458984375,1); } 0.2 finish_test |