Skip to content

Commit 01ded58

Browse files
committed
Simplify float string conversion function further
We now have a really simple function signature: pub fn from_str_radix_float<T: Float>(src: &str, radix: uint) -> Option<T> By removing some of the arguments, we remove the possibility of some invalid states.
1 parent 84f4b58 commit 01ded58

File tree

3 files changed

+115
-161
lines changed

3 files changed

+115
-161
lines changed

src/libstd/num/f32.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -359,8 +359,8 @@ pub fn to_str_exp_digits(num: f32, dig: uint, upper: bool) -> String {
359359
/// `None` if the string did not represent a valid number. Otherwise,
360360
/// `Some(n)` where `n` is the floating-point number represented by `[num]`.
361361
#[inline]
362-
pub fn from_str_hex(num: &str) -> Option<f32> {
363-
strconv::from_str_float(num, 16u, true, strconv::ExpBin)
362+
pub fn from_str_hex(src: &str) -> Option<f32> {
363+
strconv::from_str_radix_float(src, 16u)
364364
}
365365

366366
impl FromStr for f32 {
@@ -390,8 +390,8 @@ impl FromStr for f32 {
390390
/// `None` if the string did not represent a valid number. Otherwise,
391391
/// `Some(n)` where `n` is the floating-point number represented by `num`.
392392
#[inline]
393-
fn from_str(val: &str) -> Option<f32> {
394-
strconv::from_str_float(val, 10u, true, strconv::ExpDec)
393+
fn from_str(src: &str) -> Option<f32> {
394+
strconv::from_str_radix_float(src, 10u)
395395
}
396396
}
397397

@@ -414,8 +414,8 @@ impl num::FromStrRadix for f32 {
414414
/// `None` if the string did not represent a valid number. Otherwise,
415415
/// `Some(n)` where `n` is the floating-point number represented by `num`.
416416
#[inline]
417-
fn from_str_radix(val: &str, rdx: uint) -> Option<f32> {
418-
strconv::from_str_float(val, rdx, false, strconv::ExpNone)
417+
fn from_str_radix(src: &str, radix: uint) -> Option<f32> {
418+
strconv::from_str_radix_float(src, radix)
419419
}
420420
}
421421

src/libstd/num/f64.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ pub fn to_str_exp_digits(num: f64, dig: uint, upper: bool) -> String {
368368
/// `Some(n)` where `n` is the floating-point number represented by `[num]`.
369369
#[inline]
370370
pub fn from_str_hex(num: &str) -> Option<f64> {
371-
strconv::from_str_float(num, 16u, true, strconv::ExpBin)
371+
strconv::from_str_radix_float(num, 16u)
372372
}
373373

374374
impl FromStr for f64 {
@@ -399,7 +399,7 @@ impl FromStr for f64 {
399399
/// `Some(n)` where `n` is the floating-point number represented by `num`.
400400
#[inline]
401401
fn from_str(val: &str) -> Option<f64> {
402-
strconv::from_str_float(val, 10u, true, strconv::ExpDec)
402+
strconv::from_str_radix_float(val, 10u)
403403
}
404404
}
405405

@@ -422,8 +422,8 @@ impl num::FromStrRadix for f64 {
422422
/// `None` if the string did not represent a valid number. Otherwise,
423423
/// `Some(n)` where `n` is the floating-point number represented by `num`.
424424
#[inline]
425-
fn from_str_radix(val: &str, rdx: uint) -> Option<f64> {
426-
strconv::from_str_float(val, rdx, false, strconv::ExpNone)
425+
fn from_str_radix(val: &str, radix: uint) -> Option<f64> {
426+
strconv::from_str_radix_float(val, radix)
427427
}
428428
}
429429

src/libstd/num/strconv.rs

+105-151
Original file line numberDiff line numberDiff line change
@@ -424,69 +424,18 @@ pub fn float_to_str_common<T: Float>(
424424
// Some constants for from_str_bytes_common's input validation,
425425
// they define minimum radix values for which the character is a valid digit.
426426
static DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u;
427-
static DIGIT_I_RADIX: uint = ('i' as uint) - ('a' as uint) + 11u;
428427
static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u;
429428

430-
/**
431-
* Parses a string as a number. This is meant to
432-
* be a common base implementation for all numeric string conversion
433-
* functions like `from_str()` or `from_str_radix()`.
434-
*
435-
* # Arguments
436-
* - `src` - The string to parse.
437-
* - `radix` - Which base to parse the number as. Accepts 2-36.
438-
* - `special` - Whether to accept special values like `inf`
439-
* and `NaN`. Can conflict with `radix`, see Failure.
440-
* - `exponent` - Which exponent format to accept. Options are:
441-
* - `ExpNone`: No Exponent, accepts just plain numbers like `42` or
442-
* `-8.2`.
443-
* - `ExpDec`: Accepts numbers with a decimal exponent like `42e5` or
444-
* `8.2E-2`. The exponent string itself is always base 10.
445-
* Can conflict with `radix`, see Failure.
446-
* - `ExpBin`: Accepts numbers with a binary exponent like `42P-8` or
447-
* `FFp128`. The exponent string itself is always base 10.
448-
* Can conflict with `radix`, see Failure.
449-
*
450-
* # Return value
451-
* Returns `Some(n)` if `buf` parses to a number n without overflowing, and
452-
* `None` otherwise, depending on the constraints set by the remaining
453-
* arguments.
454-
*
455-
* # Failure
456-
* - Fails if `radix` < 2 or `radix` > 36.
457-
* - Fails if `radix` > 14 and `exponent` is `ExpDec` due to conflict
458-
* between digit and exponent sign `'e'`.
459-
* - Fails if `radix` > 25 and `exponent` is `ExpBin` due to conflict
460-
* between digit and exponent sign `'p'`.
461-
* - Fails if `radix` > 18 and `special == true` due to conflict
462-
* between digit and lowest first character in `inf` and `NaN`, the `'i'`.
463-
*/
464-
pub fn from_str_float<T: Float>(
465-
src: &str, radix: uint, special: bool, exponent: ExponentFormat,
466-
) -> Option<T> {
467-
match exponent {
468-
ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e'
469-
=> panic!("from_str_bytes_common: radix {} incompatible with \
470-
use of 'e' as decimal exponent", radix),
471-
ExpBin if radix >= DIGIT_P_RADIX // binary exponent 'p'
472-
=> panic!("from_str_bytes_common: radix {} incompatible with \
473-
use of 'p' as binary exponent", radix),
474-
_ if special && radix >= DIGIT_I_RADIX // first digit of 'inf'
475-
=> panic!("from_str_bytes_common: radix {} incompatible with \
476-
special values 'inf' and 'NaN'", radix),
477-
_ if (radix as int) < 2
478-
=> panic!("from_str_bytes_common: radix {} to low, \
479-
must lie in the range [2, 36]", radix),
480-
_ if (radix as int) > 36
481-
=> panic!("from_str_bytes_common: radix {} to high, \
482-
must lie in the range [2, 36]", radix),
483-
_ => ()
484-
}
429+
pub fn from_str_radix_float<T: Float>(src: &str, radix: uint) -> Option<T> {
430+
assert!(radix >= 2 && radix <= 36,
431+
"from_str_radix_float: must lie in the range `[2, 36]` - found {}",
432+
radix);
485433

486434
let _0: T = num::zero();
487435
let _1: T = num::one();
488-
let radix_gen: T = num::cast(radix as int).unwrap();
436+
let radix_t: T = num::cast(radix as int).unwrap();
489437

438+
// Special values
490439
match src {
491440
"inf" => return Some(Float::infinity()),
492441
"-inf" => return Some(Float::neg_infinity()),
@@ -501,125 +450,131 @@ pub fn from_str_float<T: Float>(
501450
(Some(_), _) => (true, src),
502451
};
503452

504-
// Initialize accumulator with signed zero for floating point parsing to
505-
// work
506-
let mut accum = if is_positive { _0 } else { -_1 };
507-
let mut last_accum = accum; // Necessary to detect overflow
508-
let mut cs = src.chars().enumerate();
509-
let mut exp = None::<(char, uint)>;
453+
// The significand to accumulate
454+
let mut sig = if is_positive { _0 } else { -_1 };
455+
// Necessary to detect overflow
456+
let mut prev_sig = sig;
457+
let mut cs = src.chars().enumerate();
458+
// Exponent prefix and exponent index offset
459+
let mut exp_info = None::<(char, uint)>;
510460

511-
// Parse integer part of number
461+
// Parse the integer part of the significand
512462
for (i, c) in cs {
513-
match c {
514-
'e' | 'E' | 'p' | 'P' => {
515-
exp = Some((c, i + 1));
516-
break; // start of exponent
517-
},
518-
'.' => {
519-
break; // start of fractional part
520-
},
521-
c => match c.to_digit(radix) {
522-
Some(digit) => {
523-
// shift accum one digit left
524-
accum = accum * radix_gen;
525-
526-
// add/subtract current digit depending on sign
527-
if is_positive {
528-
accum = accum + num::cast(digit as int).unwrap();
529-
} else {
530-
accum = accum - num::cast(digit as int).unwrap();
531-
}
463+
match c.to_digit(radix) {
464+
Some(digit) => {
465+
// shift significand one digit left
466+
sig = sig * radix_t;
467+
468+
// add/subtract current digit depending on sign
469+
if is_positive {
470+
sig = sig + num::cast(digit as int).unwrap();
471+
} else {
472+
sig = sig - num::cast(digit as int).unwrap();
473+
}
532474

533-
// Detect overflow by comparing to last value, except
534-
// if we've not seen any non-zero digits.
535-
if last_accum != _0 {
536-
if is_positive && accum <= last_accum { return Some(Float::infinity()); }
537-
if !is_positive && accum >= last_accum { return Some(Float::neg_infinity()); }
538-
539-
// Detect overflow by reversing the shift-and-add process
540-
if is_positive &&
541-
(last_accum != ((accum - num::cast(digit as int).unwrap()) / radix_gen)) {
542-
return Some(Float::infinity());
543-
}
544-
if !is_positive &&
545-
(last_accum != ((accum + num::cast(digit as int).unwrap()) / radix_gen)) {
546-
return Some(Float::neg_infinity());
547-
}
548-
}
549-
last_accum = accum;
475+
// Detect overflow by comparing to last value, except
476+
// if we've not seen any non-zero digits.
477+
if prev_sig != _0 {
478+
if is_positive && sig <= prev_sig
479+
{ return Some(Float::infinity()); }
480+
if !is_positive && sig >= prev_sig
481+
{ return Some(Float::neg_infinity()); }
482+
483+
// Detect overflow by reversing the shift-and-add process
484+
let digit: T = num::cast(digit as int).unwrap();
485+
if is_positive && (prev_sig != ((sig - digit) / radix_t))
486+
{ return Some(Float::infinity()); }
487+
if !is_positive && (prev_sig != ((sig + digit) / radix_t))
488+
{ return Some(Float::neg_infinity()); }
489+
}
490+
prev_sig = sig;
491+
},
492+
None => match c {
493+
'e' | 'E' | 'p' | 'P' => {
494+
exp_info = Some((c, i + 1));
495+
break; // start of exponent
496+
},
497+
'.' => {
498+
break; // start of fractional part
550499
},
551-
None => {
552-
return None; // invalid number
500+
_ => {
501+
return None;
553502
},
554503
},
555504
}
556505
}
557506

558-
// Parse fractional part of number
559-
// Skip if already reached start of exponent
560-
if exp.is_none() {
507+
// If we are not yet at the exponent parse the fractional
508+
// part of the significand
509+
if exp_info.is_none() {
561510
let mut power = _1;
562511
for (i, c) in cs {
563-
match c {
564-
'e' | 'E' | 'p' | 'P' => {
565-
exp = Some((c, i + 1));
566-
break; // start of exponent
512+
match c.to_digit(radix) {
513+
Some(digit) => {
514+
let digit: T = num::cast(digit).unwrap();
515+
// Decrease power one order of magnitude
516+
power = power / radix_t;
517+
// add/subtract current digit depending on sign
518+
sig = if is_positive {
519+
sig + digit * power
520+
} else {
521+
sig - digit * power
522+
};
523+
// Detect overflow by comparing to last value
524+
if is_positive && sig < prev_sig
525+
{ return Some(Float::infinity()); }
526+
if !is_positive && sig > prev_sig
527+
{ return Some(Float::neg_infinity()); }
528+
prev_sig = sig;
567529
},
568-
c => match c.to_digit(radix) {
569-
Some(digit) => {
570-
let digit: T = num::cast(digit).unwrap();
571-
572-
// Decrease power one order of magnitude
573-
power = power / radix_gen;
574-
// add/subtract current digit depending on sign
575-
accum = if is_positive {
576-
accum + digit * power
577-
} else {
578-
accum - digit * power
579-
};
580-
// Detect overflow by comparing to last value
581-
if is_positive && accum < last_accum { return Some(Float::infinity()); }
582-
if !is_positive && accum > last_accum { return Some(Float::neg_infinity()); }
583-
last_accum = accum;
530+
None => match c {
531+
'e' | 'E' | 'p' | 'P' => {
532+
exp_info = Some((c, i + 1));
533+
break; // start of exponent
584534
},
585-
None => {
535+
_ => {
586536
return None; // invalid number
587537
},
588538
},
589539
}
590540
}
591541
}
592542

593-
let multiplier = match exp {
594-
None => {
595-
_1 // no exponent
596-
},
543+
// Parse and calculate the exponent
544+
let exp = match exp_info {
597545
Some((c, offset)) => {
598-
let base: T = match (c, exponent) {
599-
// c is never _ so don't need to handle specially
600-
('e', ExpDec) | ('E', ExpDec) => num::cast(10u).unwrap(),
601-
('p', ExpBin) | ('P', ExpBin) => num::cast(2u).unwrap(),
602-
_ => return None, // char doesn't fit given exponent format
546+
let base: T = match c {
547+
'E' | 'e' if radix == 10 => num::cast(10u).unwrap(),
548+
'P' | 'p' if radix == 16 => num::cast(2u).unwrap(),
549+
_ => return None,
603550
};
604-
// parse remaining string as decimal integer
605-
let exp = from_str::<int>(src[offset..]);
606-
match exp {
607-
Some(exp_pow) if exp_pow < 0 => {
608-
_1 / num::pow(base, (-exp_pow.to_int().unwrap()) as uint)
609-
},
610-
Some(exp_pow) => {
611-
num::pow(base, exp_pow.to_int().unwrap() as uint)
612-
},
613-
None => {
614-
return None; // invalid exponent
615-
},
551+
552+
// Parse the exponent as decimal integer
553+
let src = src[offset..];
554+
let (is_positive, exp) = match src.slice_shift_char() {
555+
(Some('-'), src) => (false, from_str::<uint>(src)),
556+
(Some('+'), src) => (true, from_str::<uint>(src)),
557+
(Some(_), _) => (true, from_str::<uint>(src)),
558+
(None, _) => return None,
559+
};
560+
561+
match (is_positive, exp) {
562+
(true, Some(exp)) => num::pow(base, exp),
563+
(false, Some(exp)) => _1 / num::pow(base, exp),
564+
(_, None) => return None,
616565
}
617566
},
567+
None => _1, // no exponent
618568
};
619-
Some(accum * multiplier)
569+
570+
Some(sig * exp)
620571
}
621572

622573
pub fn from_str_radix_int<T: Int>(src: &str, radix: uint) -> Option<T> {
574+
assert!(radix >= 2 && radix <= 36,
575+
"from_str_radix_int: must lie in the range `[2, 36]` - found {}",
576+
radix);
577+
623578
fn cast<T: Int>(x: uint) -> T {
624579
num::cast(x).unwrap()
625580
}
@@ -687,10 +642,9 @@ mod test {
687642
assert_eq!(u, None);
688643
let s : Option<i16> = from_str_radix_int("80000", 10);
689644
assert_eq!(s, None);
690-
let f : Option<f32> = from_str_float(
691-
"10000000000000000000000000000000000000000", 10, false, ExpNone);
645+
let f : Option<f32> = from_str_radix_float("10000000000000000000000000000000000000000", 10);
692646
assert_eq!(f, Some(Float::infinity()))
693-
let fe : Option<f32> = from_str_float("1e40", 10, false, ExpDec);
647+
let fe : Option<f32> = from_str_radix_float("1e40", 10);
694648
assert_eq!(fe, Some(Float::infinity()))
695649
}
696650
}

0 commit comments

Comments
 (0)