blob: 9c82f4d591e008a67ba47ab4667a3e9f6876ce08 [file]
#!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<<i) FROM i2, i1),
fp(n,r) AS (SELECT n, ieee754_from_int(n) FROM fpint)
SELECT n, r FROM fp WHERE r<>(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<<i) FROM i2, i1),
fp(r) AS (SELECT ieee754_from_int(n) FROM fpint)
SELECT r FROM fp WHERE r<>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 ''