#!sqlite3 # # 2026-03-01 # # 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. # #*********************************************************************** # # Floating-point <-> text conversions # # FAILURES IN THIS SCRIPT ARE NOT NECESSARILY THE FAULT OF SQLITE. # # Some of the tests below use the system strtod() function as # an oracle of truth. These tests assume that the system strtod() # is always correct. That is the case for Win11, Macs, most Linux # boxes and so forth. But it possible to find a machine for which # is not true. (One example, is Macs from around 2005.) On such # machines, some of these tests might fail. # # So, in other words, a failure in any of the tests below does not # necessarily mean that SQLite is wrong. It might mean that the # strtod() function in the standard library of the machine on which # the test is running is wrong. # # Verify that binary64 -> text -> binary64 conversions round-trip # successfully for 98,256 different edge-case binary64 values. The # query result is all cases that do not round-trip without change, # and so the query result should be an empty set. # .testcase 100 .mode list WITH i1(i) AS (VALUES(0) UNION ALL SELECT i+1 FROM i1 WHERE i<15), i2(j) AS (VALUES(0) UNION ALL SELECT j+1 FROM i2 WHERE j<0x7fe), i3(k) AS (VALUES(0x0000000000000000), (0x000ffffffffffff0), (0x0008080808080800)), fpint(n) AS (SELECT (j<<52)+i+k FROM i2, i1, i3), fp(n,r) AS (SELECT n, ieee754_from_int(n) FROM fpint) SELECT n, r FROM fp WHERE r<>(0.0 + (r||'')); .check '' # Another batch of 106,444 edge cases: All postiive floating point # values that have only a single bit set in the mantissa part of the # number. # .testcase 110 WITH i1(i) AS (VALUES(0) UNION ALL SELECT i+1 FROM i1 WHERE i<51), i2(j) AS (VALUES(0) UNION ALL SELECT j+1 FROM i2 WHERE j<0x7fe), fpint(n) AS (SELECT (j<<52)+(1<(0.0 + (r||'')); .check '' # Verify that text -> binary64 conversions agree with system strtod(). # for 98,256 different edge-cases. # .testcase 200 .mode list WITH i1(i) AS (VALUES(0) UNION ALL SELECT i+1 FROM i1 WHERE i<15), i2(j) AS (VALUES(0) UNION ALL SELECT j+1 FROM i2 WHERE j<0x7fe), i3(k) AS (VALUES(0x0000000000000000), (0x000ffffffffffff0), (0x0008080808080800)), fpint(n) AS (SELECT (j<<52)+i+k FROM i2, i1, i3), fp(r) AS (SELECT ieee754_from_int(n) FROM fpint) SELECT r FROM fp WHERE r<>strtod(r||''); .check '' # Another batch of 106,444 edge cases: All postiive floating point # values that have only a single bit set in the mantissa part of the # number. # .testcase 210 WITH i1(i) AS (VALUES(0) UNION ALL SELECT i+1 FROM i1 WHERE i<51), i2(j) AS (VALUES(0) UNION ALL SELECT j+1 FROM i2 WHERE j<0x7fe), fpint(n) AS (SELECT (j<<52)+(1<strtod(r||''); .check '' # Comparing SQLite's text-to-double conversion against strtod() # for 200,000 random floating-point literals. # .param set $N 50_000 .testcase 300 WITH RECURSIVE fp(n,x) AS MATERIALIZED ( VALUES(1,'1234.5789') UNION ALL SELECT n+1, format('%.*s.%.*se%+d', (n%3)+1, random()%1000, abs(random()%16)+1, abs(random()), random()%308) FROM fp WHERE n<$N ) SELECT x FROM fp WHERE (x+0.0)<>strtod(x); .check '' .testcase 301 WITH RECURSIVE fp(n,x) AS MATERIALIZED ( VALUES(1,'1234.5789') UNION ALL SELECT n+1, format('%.*s.%.*s', (n%3)+1, random()%1000, abs(random()%16)+1, abs(random())) FROM fp WHERE n<$N ) SELECT x FROM fp WHERE (x+0.0)<>strtod(x); .check '' .testcase 302 WITH RECURSIVE fp(n,x) AS MATERIALIZED ( VALUES(1,'1234.5789') UNION ALL SELECT n+1, format('%.*s.%.*s', abs(random()%7)+1, random(), abs(random()%10)+1, abs(random())) FROM fp WHERE n<$N ) SELECT x FROM fp WHERE (x+0.0)<>strtod(x); .check '' .testcase 303 WITH RECURSIVE fp(n,x) AS MATERIALIZED ( VALUES(1,'1234.5789') UNION ALL SELECT n+1, format('%de%+03d', random(), random()%308) FROM fp WHERE n<$N ) SELECT x FROM fp WHERE (x+0.0)<>strtod(x); .check '' # Another 500,000 comparisions between SQLite and strtod() from completely # random floating point literals. # .testcase 310 WITH RECURSIVE fp(n,x) AS MATERIALIZED ( SELECT 1, format('%+.*g',abs(random()%18), ieee754_from_int(random())) UNION ALL SELECT n+1, format('%+.*g',abs(random()%18), ieee754_from_int(random())) FROM fp WHERE n<$N*2 ) SELECT x FROM fp WHERE (x+0.0)<>strtod(x); .check '' .testcase 311 WITH RECURSIVE fp(n,x) AS MATERIALIZED ( SELECT 1, format('%+.*g',abs(random()%18), ieee754_from_int(random())) UNION ALL SELECT n+1, format('%+.*g',abs(random()%18), ieee754_from_int(random())) FROM fp WHERE n<$N*2 ) SELECT x FROM fp WHERE (x+0.0)<>strtod(x); .check '' .testcase 312 WITH RECURSIVE fp(n,x) AS MATERIALIZED ( SELECT 1, format('%+.*g',abs(random()%18), ieee754_from_int(random())) UNION ALL SELECT n+1, format('%+.*g',abs(random()%18), ieee754_from_int(random())) FROM fp WHERE n<$N*2 ) SELECT x FROM fp WHERE (x+0.0)<>strtod(x); .check '' .testcase 313 WITH RECURSIVE fp(n,x) AS MATERIALIZED ( SELECT 1, format('%+.*g',abs(random()%18), ieee754_from_int(random())) UNION ALL SELECT n+1, format('%+.*g',abs(random()%18), ieee754_from_int(random())) FROM fp WHERE n<$N*2 ) SELECT x FROM fp WHERE (x+0.0)<>strtod(x); .check '' .testcase 314 WITH RECURSIVE fp(n,x) AS MATERIALIZED ( SELECT 1, format('%+.*g',abs(random()%18), ieee754_from_int(random())) UNION ALL SELECT n+1, format('%+.*g',abs(random()%18), ieee754_from_int(random())) FROM fp WHERE n<$N*2 ) SELECT x FROM fp WHERE (x+0.0)<>strtod(x); .check ''