Add short option to float_to_binary/list#2240
Conversation
|
Fwiw - ryu here https://github.com/petermm/AtomVM/tree/feature/ryu-float-formatting - it does fit, but do think a smaller implementation makes sense. maybe we want ryu in future for generic_unix/wasm. ampcode review: PR Review: Add
|
Grisu3 requires a fallback the 0.5% of the times, while Ryu doesn't. I don't think it is worth the footprint in terms of binary size.
This happens around with the 0.5% of the conversions.
This is a non-issue in AtomVM. Terms never store NaNs, we awlays check isinite() before making a new float term. |
|
I know it repeats the infinite thing - just ignore;-) PR Review Round 2: Add
|
| Area | Status |
|---|---|
fixup_g_format() as a %g normalizer |
✅ Correct — handles E→e, ensures ., strips + and leading zeros |
Single-digit scientific fix (len==1 else branch) |
✅ Correct — "1" d_exp=3 → "1.0e3" |
| Option parsing ("last option wins") | ✅ Matches OTP — [short, {decimals,4}] → decimals, [{decimals,4}, short] → short |
fixup_g_format() shared for double+float32 |
✅ Safe — float32 %.9g already uses scientific for large values |
unlocalized_snprintf.h doc fix |
✅ Properly scoped |
| Test coverage | ✅ Much better — negative zero, extremes, fallback values, badarg combos |
erlang.erl documentation |
✅ Clear about Grisu3 limitations and 32-bit behavior |
| Memory safety | ✅ No overflow found |
| Elixir library updates | ✅ Clean |
Fixed the last issue. |
| % SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later | ||
| % | ||
|
|
||
| -module(float2list_short). |
There was a problem hiding this comment.
Do we really need to duplicate the float2bin test here?
There was a problem hiding this comment.
Good point, I will merge the 2 tests
Introduce shortest-roundtrip formatting for float_to_binary/2 and
float_to_list/2 via the [short] option, matching Erlang/OTP
behavior.
- Add float_utils with Grisu3 for doubles and best-effort snprintf
for 32-bit floats. Grisu3 was chosen over Ryu (used by
Erlang/OTP) for its compact code and small powers table, saving
flash space on embedded targets.
- Add unlocalized_snprintf to fix locale-dependent decimal
separator bug in float formatting.
- Extend option ranges to {decimals, 0..253} and
{scientific, 0..249} to match Erlang/OTP limits.
Signed-off-by: Davide Bettio <davide@uninstall.it>
Now that float_to_binary/2 and float_to_list/2 support [short],
replace the {decimals, 17} + compact workaround with [:short].
Signed-off-by: Davide Bettio <davide@uninstall.it>
Merge fixes, features, and optimizations from release-0.7, including: - Add signature-driven code loader (#2229) - Add UART support on generic_unix via POSIX termios NIFs (#2243) - Add erlang node/1 BIF (#2225) - Add erts_internal:cmp_term/2 NIF and fix map type ordering (#2226) - Add short option to float_to_binary/list (#2240) - Add locale-independent float parsing and fix overflow (#2246) - JIT: add RISC-V 64-bit backend (#2231) - JIT: add DWARF debug information support (#1910) - JIT x86: instruction encoding optimizations (#2234) - JIT: remove redundant AND when untagging integers (#2235) - Fix bug in bs_match get_tail handling (#2242) - Fix binary encoding of int:24 and others (#2230) - Fix cancel_timer/1 spec and documentation (#2244) - Resurrect opcodes emitted with no_bs_create_bin (#2245) - Fix flaky tests related to GitHub DNS Resolver (#2232) - Add git guide (#2106)
[short]option for shortest-roundtrip float formatting, matchingErlang/OTP behavior. Uses Grisu3 for 64-bit doubles, best-effort snprintf
for 32-bit floats.
,instead of.insome locales) via
unlocalized_snprintf.{decimals, 0..253}and{scientific, 0..249}to match Erlang/OTP limits.
Fixes #2189
Why Grisu3 over Ryu
Erlang/OTP uses Ryu, which guarantees shortest output for all doubles.
Grisu3 produces the shortest output for ~99.5% of values and falls back
to a longer (but correct) 17-digit representation for the rest.
Grisu3 was chosen for its smaller binary footprint on embedded targets:
Stripped AtomVM binary (x86_64, -Os):
Before this PR: 653,768 bytes
With Grisu3:657,864 bytes(+4 KB)
With Ryu (full):756,168 bytes(+98 KB)
With Ryu (small): 747,976 bytes(+90 KB)
Differences with Erlang/OTP
For most values the output is identical. The Grisu3 fallback (~0.5% of
doubles) produces longer output:
Tests:
Grisu3 implementation has been tested against a dataset with milions of
doubles with no errors.
These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).
SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later