fix(numeric): replace decompose_float with stdlib formatting in format_float#6366
fix(numeric): replace decompose_float with stdlib formatting in format_float#6366Chronolapse411 wants to merge 1 commit intotursodatabase:mainfrom
Conversation
…t_float
The custom decompose_float algorithm used DoubleDouble arithmetic for digit
extraction, which produced different last-digit rounding than SQLite's
sqlite3FpDecode. This caused data corruption when formatted floats were
re-parsed during text affinity conversion (e.g., CAST to TEXT).
Replace with Rust's standard library formatting (Grisu3/Dragon4), which
matches the digit sequences produced by SQLite's internal float-to-text
conversion. The fix changes format_float and format_float_scientific to
use format!("{:.14e}") for 15-digit precision digit extraction, then
reconstructs the %.15g-style output (fixed vs scientific notation switch
at exponent boundaries [-4, 14]).
Verified with:
- All 1504 turso_core unit tests pass
- Differential simulator seed 6017 (previously failing): PASS
- Seed sweep 6010-6025: 0 differential mismatches
Fossier: PR Auto-ClosedThis PR was automatically closed because Score BreakdownTotal Score: 36.4/100 | Confidence: 100% | Outcome: DENY
AppealIf you believe this is a mistake, please open an issue to request manual review. A maintainer can vouch for you by adding your username to the You can also reach the maintainers at: https://discord.gg/mNUkCHs2R to appeal this decision |
Merging this PR will degrade performance by 48.47%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ⚡ | Simulation | format_float_infinity |
1,172.8 ns | 984.4 ns | +19.13% |
| ❌ | Simulation | format_float_integer |
5.3 µs | 10.3 µs | -48.47% |
| ⚡ | Simulation | format_float_neg_infinity |
1,201.1 ns | 954.4 ns | +25.84% |
| ❌ | Simulation | format_float_negative |
5.4 µs | 7.6 µs | -29.55% |
| ❌ | Simulation | format_float_precision_edge |
5.2 µs | 7.6 µs | -31.94% |
| ❌ | Simulation | format_float_very_small |
6.3 µs | 9.1 µs | -30.7% |
| ❌ | Simulation | format_float_simple |
5.3 µs | 7.6 µs | -29.67% |
| ❌ | Simulation | format_float_scientific_needed |
7.1 µs | 9.1 µs | -21.97% |
| ⚡ | Simulation | format_float_zero |
1,107.2 ns | 859.2 ns | +28.87% |
| ❌ | Simulation | format_float_very_large |
6.2 µs | 9.2 µs | -32.54% |
| ❌ | Simulation | length_float |
7.2 µs | 9 µs | -20.46% |
Comparing Chronolapse411:fix/format-float-precision (b71b0ba) with main (9b3dc16)
Footnotes
-
105 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
Summary
Fix data corruption in float-to-text conversion by replacing the custom
decompose_floatdigit-extraction algorithm with Rust's standard library formatting.Fixes #6365
Problem
format_float()incore/numeric/mod.rsuses a customdecompose_floatalgorithm based onDoubleDoublearithmetic for extracting decimal digits from floating-point numbers. This algorithm produces different last-digit rounding than SQLite's internalsqlite3FpDecode, which useslong double/ 80-bit extended precision.When Turso formats a float (e.g., during text affinity conversion via
CAST(x AS TEXT)), the resulting string can differ from what SQLite produces:-8487739174.3030205-8487739174.3030205-8487739174.303029.93e-3229.93071948140905e-3229.93071948140906e-322When these differently-formatted strings are re-parsed into floats, the resulting bit patterns differ (up to 7 ULPs), causing differential test failures and real data corruption.
Fix
Replace
decompose_float-based digit extraction with Rust'sformat!("{:.14e}"), which uses industry-standard Grisu3/Dragon4 algorithms. These produce the same digit sequences as SQLite'ssqlite3FpDecodeand C'sprintf("%.15g").The new
format_floatimplementation:format!("{:.14e}", v)to get 15 significant digits%g-style fixed vs. scientific notation switch (exponent range[-4, 14]).0for integer-valued floatsNaN,±Inf, and0.0edge casesAlso removes the now-unused
FloatPartsenum anddecompose_floatfunction (dead code cleanup).Verification
turso_coreunit tests pass (0 failures)-D warnings(0 warnings)test_decode_float,test_format_float_matches_sqlite,test_format_float_edge_cases)simulation succeeded