@@ -230,18 +230,15 @@ fn convert<T: RawFloat>(mut decimal: Decimal) -> Result<T, ParseFloatError> {
230
230
if let Some ( x) = trivial_cases ( & decimal) {
231
231
return Ok ( x) ;
232
232
}
233
- // AlgorithmM and AlgorithmR both compute approximately `f * 10^e`.
234
- let max_digits = decimal. integral . len ( ) + decimal. fractional . len ( ) +
235
- decimal. exp . abs ( ) as usize ;
236
233
// Remove/shift out the decimal point.
237
234
let e = decimal. exp - decimal. fractional . len ( ) as i64 ;
238
235
if let Some ( x) = algorithm:: fast_path ( decimal. integral , decimal. fractional , e) {
239
236
return Ok ( x) ;
240
237
}
241
238
// Big32x40 is limited to 1280 bits, which translates to about 385 decimal digits.
242
- // If we exceed this, perhaps while calculating `f * 10^e` in Algorithm R or Algorithm M,
243
- // we'll crash. So we error out before getting too close, with a generous safety margin.
244
- if max_digits > 375 {
239
+ // If we exceed this, we'll crash, so we error out before getting too close (within 10^10).
240
+ let upper_bound = bound_intermediate_digits ( & decimal , e ) ;
241
+ if upper_bound > 375 {
245
242
return Err ( pfe_invalid ( ) ) ;
246
243
}
247
244
let f = digits_to_big ( decimal. integral , decimal. fractional ) ;
@@ -251,7 +248,7 @@ fn convert<T: RawFloat>(mut decimal: Decimal) -> Result<T, ParseFloatError> {
251
248
// FIXME These bounds are rather conservative. A more careful analysis of the failure modes
252
249
// of Bellerophon could allow using it in more cases for a massive speed up.
253
250
let exponent_in_range = table:: MIN_E <= e && e <= table:: MAX_E ;
254
- let value_in_range = max_digits <= T :: max_normal_digits ( ) ;
251
+ let value_in_range = upper_bound <= T :: max_normal_digits ( ) as u64 ;
255
252
if exponent_in_range && value_in_range {
256
253
Ok ( algorithm:: bellerophon ( & f, e) )
257
254
} else {
@@ -288,13 +285,36 @@ fn simplify(decimal: &mut Decimal) {
288
285
}
289
286
}
290
287
288
+ /// Quick and dirty upper bound on the size (log10) of the largest value that Algorithm R and
289
+ /// Algorithm M will compute while working on the given decimal.
290
+ fn bound_intermediate_digits ( decimal : & Decimal , e : i64 ) -> u64 {
291
+ // We don't need to worry too much about overflow here thanks to trivial_cases() and the
292
+ // parser, which filter out the most extreme inputs for us.
293
+ let f_len: u64 = decimal. integral . len ( ) as u64 + decimal. fractional . len ( ) as u64 ;
294
+ if e >= 0 {
295
+ // In the case e >= 0, both algorithms compute about `f * 10^e`. Algorithm R proceeds to
296
+ // do some complicated calculations with this but we can ignore that for the upper bound
297
+ // because it also reduces the fraction beforehand, so we have plenty of buffer there.
298
+ f_len + ( e as u64 )
299
+ } else {
300
+ // If e < 0, Algorithm R does roughly the same thing, but Algorithm M differs:
301
+ // It tries to find a positive number k such that `f << k / 10^e` is an in-range
302
+ // significand. This will result in about `2^53 * f * 10^e` < `10^17 * f * 10^e`.
303
+ // One input that triggers this is 0.33...33 (375 x 3).
304
+ f_len + ( e. abs ( ) as u64 ) + 17
305
+ }
306
+ }
307
+
291
308
/// Detect obvious overflows and underflows without even looking at the decimal digits.
292
309
fn trivial_cases < T : RawFloat > ( decimal : & Decimal ) -> Option < T > {
293
310
// There were zeros but they were stripped by simplify()
294
311
if decimal. integral . is_empty ( ) && decimal. fractional . is_empty ( ) {
295
312
return Some ( T :: zero ( ) ) ;
296
313
}
297
- // This is a crude approximation of ceil(log10(the real value)).
314
+ // This is a crude approximation of ceil(log10(the real value)). We don't need to worry too
315
+ // much about overflow here because the input length is tiny (at least compared to 2^64) and
316
+ // the parser already handles exponents whose absolute value is greater than 10^18
317
+ // (which is still 10^19 short of 2^64).
298
318
let max_place = decimal. exp + decimal. integral . len ( ) as i64 ;
299
319
if max_place > T :: inf_cutoff ( ) {
300
320
return Some ( T :: infinity ( ) ) ;
0 commit comments