about summary refs log tree commit diff
path: root/library/core/src/num
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-04-25 07:05:50 +0000
committerbors <bors@rust-lang.org>2023-04-25 07:05:50 +0000
commit91b61a4ad618c1abc2af43a58695de185ef1e513 (patch)
treefcd298dc7aa39c1dbf800607af4c67d28dbd1a5c /library/core/src/num
parented3ddb375bf18099a885605efba94dc1e50379d7 (diff)
parentb0a85d614d5cc1918bc9eef9abbf78d5c585cbef (diff)
downloadrust-91b61a4ad618c1abc2af43a58695de185ef1e513.tar.gz
rust-91b61a4ad618c1abc2af43a58695de185ef1e513.zip
Auto merge of #110389 - mazong1123:add-shortcut-for-grisu3, 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](https://github.com/dotnet/coreclr/pull/14646#issuecomment-350942050). This shortcut has been shipped long time ago and has been proved working.

Fix #110129
Diffstat (limited to 'library/core/src/num')
-rw-r--r--library/core/src/num/flt2dec/strategy/grisu.rs16
1 files changed, 16 insertions, 0 deletions
diff --git a/library/core/src/num/flt2dec/strategy/grisu.rs b/library/core/src/num/flt2dec/strategy/grisu.rs
index ed3e0edaff2..b9f0d114c6a 100644
--- a/library/core/src/num/flt2dec/strategy/grisu.rs
+++ b/library/core/src/num/flt2dec/strategy/grisu.rs
@@ -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).