Skip to content

float: Fix panic at max exponential precision#154052

Open
cdown wants to merge 1 commit intorust-lang:mainfrom
cdown:cdown/2026-03-19/precision
Open

float: Fix panic at max exponential precision#154052
cdown wants to merge 1 commit intorust-lang:mainfrom
cdown:cdown/2026-03-19/precision

Conversation

@cdown
Copy link
Contributor

@cdown cdown commented Mar 18, 2026

Rust's formatting machinery allows precision values of up to u16::MAX. Exponential formatting works out the number of significant digits to use by adding one (for the integral digit before the decimal point).

This previously used usize precision, so the maximum validated precision did not overflow, but in commit fb9ce02 ("Limit formatting width and precision to 16 bits.") the precision type was narrowed to u16 without widening that addition first.

As a result an exponential precision value of 65535 is no longer handled correctly, because the digit count wraps to 0, and thus "{:.65535e}" panics in flt2dec::to_exact_exp_str with "assertion failed: ndigits > 0". Other formats (and the parser) accept values up to u16::MAX.

A naive fix would be to widen that addition back to usize, but that still does not properly address 16-bit targets, where usize is only guaranteed to be able to represent values up to u16::MAX. The real issue is that this internal API is expressed in the wrong units for the formatter.

Fix this by changing exact exponential formatting to take fractional digits internally as well, and compute the temporary significant digit bound only when sizing the scratch buffer. To support that let's also make formatted length accounting saturate so that extremely large rendered outputs do not reintroduce overflows in padding logic.

This preserves the existing intent and keeps FormattingOptions compact while making formatting work consistently again.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Mar 18, 2026
@rustbot
Copy link
Collaborator

rustbot commented Mar 18, 2026

r? @Mark-Simulacrum

rustbot has assigned @Mark-Simulacrum.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: @scottmcm, libs
  • @scottmcm, libs expanded to 8 candidates
  • Random selection from Mark-Simulacrum, scottmcm

@cdown cdown force-pushed the cdown/2026-03-19/precision branch 2 times, most recently from 71fe342 to d57cfdf Compare March 19, 2026 03:32
Rust's formatting machinery allows precision values of up to u16::MAX.
Exponential formatting works out the number of significant digits to use
by adding one (for the integral digit before the decimal point).

This previously used usize precision, so the maximum validated precision
did not overflow, but in commit fb9ce02 ("Limit formatting width
and precision to 16 bits.") the precision type was narrowed to u16
without widening that addition first.

As a result an exponential precision value of 65535 is no longer handled
correctly, because the digit count wraps to 0, and thus "{:.65535e}"
panics in flt2dec::to_exact_exp_str with "assertion failed: ndigits >
0". Other formats (and the parser) accept values up to u16::MAX.

A naive fix would be to widen that addition back to usize, but that
still does not properly address 16-bit targets, where usize is only
guaranteed to be able to represent values up to u16::MAX. The real issue
is that this internal API is expressed in the wrong units for the
formatter.

Fix this by changing exact exponential formatting to take fractional
digits internally as well, and compute the temporary significant digit
bound only when sizing the scratch buffer. To support that let's also
make formatted length accounting saturate so that extremely large
rendered outputs do not reintroduce overflows in padding logic.

This preserves the existing intent and keeps FormattingOptions compact
while making formatting work consistently again.
@cdown cdown force-pushed the cdown/2026-03-19/precision branch from d57cfdf to 77f6ba6 Compare March 20, 2026 06:23
@rustbot
Copy link
Collaborator

rustbot commented Mar 20, 2026

Some changes occurred in float parsing

cc @tgross35

@cdown cdown requested a review from hkBst March 20, 2026 06:23
/// Saturates at `usize::MAX` if the actual length is larger.
pub fn len(&self) -> usize {
self.sign.len() + self.parts.iter().map(|part| part.len()).sum::<usize>()
self.parts.iter().fold(self.sign.len(), |len, part| len.saturating_add(part.len()))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to close the nearby overflow hazard in the same code path, since we have to touch this anyway. Let me know if you want it split out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants