Skip to content

Commit

Permalink
Auto merge of rust-lang#110389 - mazong1123:add-shortcut-for-grisu3, …
Browse files Browse the repository at this point in the history
…r=Mark-Simulacrum

Add shortcut for Grisu3 algorithm.

While Grisu3 is way more faster for most numbers compare to Dragon4, the fall back to Dragon4 procedure for certain numbers could cause some performance regressions compare to use Dragon4 directly. Mitigating the regression caused by falling back is important for a largely used core library.

In Grisu3 algorithm implementation, there's a shortcut to jump out earlier when the fractional or integrals cannot meet the requirement of requested digits. This could significantly improve the performance of converting floating number to string as it falls back even without starting trying the algorithm.

The original idea is from the [.NET implementation](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Number.Grisu3.cs#L602-L615) and the code was originally added in [this PR](dotnet/coreclr#14646 (comment)). This shortcut has been shipped long time ago and has been proved working.

Fix rust-lang#110129
  • Loading branch information
bors committed Apr 25, 2023
2 parents ed3ddb3 + b0a85d6 commit 91b61a4
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 0 deletions.
27 changes: 27 additions & 0 deletions library/core/benches/num/flt2dec/strategy/grisu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,30 @@ fn bench_big_exact_inf(b: &mut Bencher) {
format_exact(black_box(&decoded), &mut buf, i16::MIN);
});
}

#[bench]
fn bench_one_exact_inf(b: &mut Bencher) {
let decoded = decode_finite(1.0);
let mut buf = [MaybeUninit::new(0); 1024];
b.iter(|| {
format_exact(black_box(&decoded), &mut buf, i16::MIN);
});
}

#[bench]
fn bench_trailing_zero_exact_inf(b: &mut Bencher) {
let decoded = decode_finite(250.000000000000000000000000);
let mut buf = [MaybeUninit::new(0); 1024];
b.iter(|| {
format_exact(black_box(&decoded), &mut buf, i16::MIN);
});
}

#[bench]
fn bench_halfway_point_exact_inf(b: &mut Bencher) {
let decoded = decode_finite(1.00000000000000011102230246251565404236316680908203125);
let mut buf = [MaybeUninit::new(0); 1024];
b.iter(|| {
format_exact(black_box(&decoded), &mut buf, i16::MIN);
});
}
16 changes: 16 additions & 0 deletions library/core/src/num/flt2dec/strategy/grisu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,22 @@ pub fn format_exact_opt<'a>(
let vint = (v.f >> e) as u32;
let vfrac = v.f & ((1 << e) - 1);

let requested_digits = buf.len();

const POW10_UP_TO_9: [u32; 10] =
[1, 10, 100, 1000, 10_000, 100_000, 1_000_000, 10_000_000, 100_000_000, 1_000_000_000];

// We deviate from the original algorithm here and do some early checks to determine if we can satisfy requested_digits.
// If we determine that we can't, we exit early and avoid most of the heavy lifting that the algorithm otherwise does.
//
// When vfrac is zero, we can easily determine if vint can satisfy requested digits:
// If requested_digits >= 11, vint is not able to exhaust the count by itself since 10^(11 -1) > u32 max value >= vint.
// If vint < 10^(requested_digits - 1), vint cannot exhaust the count.
// Otherwise, vint might be able to exhaust the count and we need to execute the rest of the code.
if (vfrac == 0) && ((requested_digits >= 11) || (vint < POW10_UP_TO_9[requested_digits - 1])) {
return None;
}

// both old `v` and new `v` (scaled by `10^-k`) has an error of < 1 ulp (Theorem 5.1).
// as we don't know the error is positive or negative, we use two approximations
// spaced equally and have the maximal error of 2 ulps (same to the shortest case).
Expand Down

0 comments on commit 91b61a4

Please sign in to comment.