Skip to content

Fix no_std panic in Byte::from_f64/from_f32 on non-finite input - #38

Open
dualfroz wants to merge 1 commit into
magiclen:masterfrom
dualfroz:fix-nostd-ceil-nonfinite-panic
Open

Fix no_std panic in Byte::from_f64/from_f32 on non-finite input#38
dualfroz wants to merge 1 commit into
magiclen:masterfrom
dualfroz:fix-nostd-ceil-nonfinite-panic

Conversation

@dualfroz

@dualfroz dualfroz commented Sep 5, 2026

Copy link
Copy Markdown

Problem

Byte::from_f64 and Byte::from_f32 are documented to return None when the
input is "too large" or not >= 0. In std builds a non-finite input such as
f64::INFINITY correctly returns None, but in no_std builds it panics:

// with `--no-default-features --features byte`
byte_unit::Byte::from_f64(f64::INFINITY);
// thread panicked at src/common.rs:39:
// called `Option::unwrap()` on a `None` value

So the same call behaves differently across the two build configurations and
breaks the documented contract in no_std.

Root cause

from_f64/from_f32 guard the input with if size >= 0.0, which rejects
NaN (since NaN >= 0.0 is false) but lets +inf through. The value is then
passed to ceil_f64/ceil_f32 in src/common.rs.

The std implementation uses v.ceil(), which returns inf; the subsequent
inf as u128/as u64 cast saturates to the maximum, and the range check then
returns None.

The no_std implementation instead does:

Decimal::from_f64(v).unwrap().ceil().to_f64().unwrap()

Decimal::from_f64 (and from_f32) returns None for non-finite values, so
.unwrap() panics. The no_std helper therefore diverges from the std one.

Fix

Make the no_std ceil_f64/ceil_f32 fall back to the input value when it is
not representable as a Decimal (i.e. non-finite), mirroring f64::ceil:

match Decimal::from_f64(v) {
    Some(d) => d.ceil().to_f64().unwrap_or(v),
    None => v,
}

With this, from_f64(f64::INFINITY) flows through the same saturating cast and
range check as in std, returning None. Finite values are unaffected.

Test

Added from_non_finite_returns_none to tests/byte.rs, asserting that
from_f64/from_f32 return None for both infinity and NaN. It passes under
--all-features and under --no-default-features --features byte (the config
that previously panicked).

Byte::from_f64/from_f32 let +inf past the size >= 0.0 guard and then called
the no_std ceil helpers, which used Decimal::from_f64(v).unwrap(). Decimal
conversion returns None for non-finite values, so this panicked in no_std
builds while std returned None. Fall back to the input value for non-finite
input so no_std matches std and the documented None contract is upheld.
@dualfroz
dualfroz force-pushed the fix-nostd-ceil-nonfinite-panic branch from e84be64 to 277dd54 Compare September 5, 2026 23:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant