]> git.lizzy.rs Git - rust.git/blob - src/libcore/num/float_macros.rs
fix spacing issue in trpl/documentation doc
[rust.git] / src / libcore / num / float_macros.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 #![doc(hidden)]
12
13 macro_rules! assert_approx_eq {
14     ($a:expr, $b:expr) => ({
15         use num::Float;
16         let (a, b) = (&$a, &$b);
17         assert!((*a - *b).abs() < 1.0e-6,
18                 "{} is not approximately equal to {}", *a, *b);
19     })
20 }
21
22 macro_rules! from_str_radix_float_impl {
23     ($T:ty) => {
24         fn from_str_radix(src: &str, radix: u32)
25                           -> Result<$T, ParseFloatError> {
26             use num::FloatErrorKind::*;
27             use num::ParseFloatError as PFE;
28
29             // Special values
30             match src {
31                 "inf"   => return Ok(Float::infinity()),
32                 "-inf"  => return Ok(Float::neg_infinity()),
33                 "NaN"   => return Ok(Float::nan()),
34                 _       => {},
35             }
36
37             let (is_positive, src) =  match src.slice_shift_char() {
38                 None             => return Err(PFE { __kind: Empty }),
39                 Some(('-', ""))  => return Err(PFE { __kind: Empty }),
40                 Some(('-', src)) => (false, src),
41                 Some((_, _))     => (true,  src),
42             };
43
44             // The significand to accumulate
45             let mut sig = if is_positive { 0.0 } else { -0.0 };
46             // Necessary to detect overflow
47             let mut prev_sig = sig;
48             let mut cs = src.chars().enumerate();
49             // Exponent prefix and exponent index offset
50             let mut exp_info = None::<(char, usize)>;
51
52             // Parse the integer part of the significand
53             for (i, c) in cs.by_ref() {
54                 match c.to_digit(radix) {
55                     Some(digit) => {
56                         // shift significand one digit left
57                         sig = sig * (radix as $T);
58
59                         // add/subtract current digit depending on sign
60                         if is_positive {
61                             sig = sig + ((digit as isize) as $T);
62                         } else {
63                             sig = sig - ((digit as isize) as $T);
64                         }
65
66                         // Detect overflow by comparing to last value, except
67                         // if we've not seen any non-zero digits.
68                         if prev_sig != 0.0 {
69                             if is_positive && sig <= prev_sig
70                                 { return Ok(Float::infinity()); }
71                             if !is_positive && sig >= prev_sig
72                                 { return Ok(Float::neg_infinity()); }
73
74                             // Detect overflow by reversing the shift-and-add process
75                             if is_positive && (prev_sig != (sig - digit as $T) / radix as $T)
76                                 { return Ok(Float::infinity()); }
77                             if !is_positive && (prev_sig != (sig + digit as $T) / radix as $T)
78                                 { return Ok(Float::neg_infinity()); }
79                         }
80                         prev_sig = sig;
81                     },
82                     None => match c {
83                         'e' | 'E' | 'p' | 'P' => {
84                             exp_info = Some((c, i + 1));
85                             break;  // start of exponent
86                         },
87                         '.' => {
88                             break;  // start of fractional part
89                         },
90                         _ => {
91                             return Err(PFE { __kind: Invalid });
92                         },
93                     },
94                 }
95             }
96
97             // If we are not yet at the exponent parse the fractional
98             // part of the significand
99             if exp_info.is_none() {
100                 let mut power = 1.0;
101                 for (i, c) in cs.by_ref() {
102                     match c.to_digit(radix) {
103                         Some(digit) => {
104                             // Decrease power one order of magnitude
105                             power = power / (radix as $T);
106                             // add/subtract current digit depending on sign
107                             sig = if is_positive {
108                                 sig + (digit as $T) * power
109                             } else {
110                                 sig - (digit as $T) * power
111                             };
112                             // Detect overflow by comparing to last value
113                             if is_positive && sig < prev_sig
114                                 { return Ok(Float::infinity()); }
115                             if !is_positive && sig > prev_sig
116                                 { return Ok(Float::neg_infinity()); }
117                             prev_sig = sig;
118                         },
119                         None => match c {
120                             'e' | 'E' | 'p' | 'P' => {
121                                 exp_info = Some((c, i + 1));
122                                 break; // start of exponent
123                             },
124                             _ => {
125                                 return Err(PFE { __kind: Invalid });
126                             },
127                         },
128                     }
129                 }
130             }
131
132             // Parse and calculate the exponent
133             let exp = match exp_info {
134                 Some((c, offset)) => {
135                     let base = match c {
136                         'E' | 'e' if radix == 10 => 10.0,
137                         'P' | 'p' if radix == 16 => 2.0,
138                         _ => return Err(PFE { __kind: Invalid }),
139                     };
140
141                     // Parse the exponent as decimal integer
142                     let src = &src[offset..];
143                     let (is_positive, exp) = match src.slice_shift_char() {
144                         Some(('-', src)) => (false, src.parse::<usize>()),
145                         Some(('+', src)) => (true,  src.parse::<usize>()),
146                         Some((_, _))     => (true,  src.parse::<usize>()),
147                         None             => return Err(PFE { __kind: Invalid }),
148                     };
149
150                     match (is_positive, exp) {
151                         (true,  Ok(exp)) => base.powi(exp as i32),
152                         (false, Ok(exp)) => 1.0 / base.powi(exp as i32),
153                         (_, Err(_))      => return Err(PFE { __kind: Invalid }),
154                     }
155                 },
156                 None => 1.0, // no exponent
157             };
158
159             Ok(sig * exp)
160         }
161     }
162 }