99//! [`PreciseNumber`] struct.
1010use std:: str:: FromStr ;
1111
12- use bigdecimal:: BigDecimal ;
13- use num_traits:: Zero ;
1412use uucore:: format:: num_parser:: { ExtendedParser , ExtendedParserError } ;
1513
16- use crate :: { hexadecimalfloat , number:: PreciseNumber } ;
14+ use crate :: number:: PreciseNumber ;
1715use uucore:: format:: ExtendedBigDecimal ;
1816
1917/// An error returned when parsing a number fails.
@@ -23,10 +21,11 @@ pub enum ParseNumberError {
2321 Nan ,
2422}
2523
26- // Compute the number of integral digits in input string. We know that the
27- // string has already been parsed correctly, so we don't need to be too
28- // careful.
29- fn compute_num_integral_digits ( input : & str , _number : & BigDecimal ) -> usize {
24+ // Compute the number of integral and fractional digits in input string,
25+ // and wrap the result in a PreciseNumber.
26+ // We know that the string has already been parsed correctly, so we don't
27+ // need to be too careful.
28+ fn compute_num_digits ( input : & str , ebd : ExtendedBigDecimal ) -> PreciseNumber {
3029 let input = input. to_lowercase ( ) ;
3130 let mut input = input. trim_start ( ) ;
3231
@@ -35,42 +34,63 @@ fn compute_num_integral_digits(input: &str, _number: &BigDecimal) -> usize {
3534 input = trimmed;
3635 }
3736
38- // Integral digits for an hex number is ill-defined.
37+ // Integral digits for any hex number is ill-defined (0 is fine as an output)
38+ // Fractional digits for an floating hex number is ill-defined, return None
39+ // as we'll totally ignore that number for precision computations.
40+ // Still return 0 for hex integers though.
3941 if input. starts_with ( "0x" ) || input. starts_with ( "-0x" ) {
40- return 0 ;
42+ return PreciseNumber {
43+ number : ebd,
44+ num_integral_digits : 0 ,
45+ num_fractional_digits : if input. contains ( "." ) || input. contains ( "p" ) {
46+ None
47+ } else {
48+ Some ( 0 )
49+ } ,
50+ } ;
4151 }
4252
4353 // Split the exponent part, if any
4454 let parts: Vec < & str > = input. split ( "e" ) . collect ( ) ;
4555 debug_assert ! ( parts. len( ) <= 2 ) ;
4656
4757 // Count all the digits up to `.`, `-` sign is included.
48- let digits : usize = match parts[ 0 ] . find ( "." ) {
58+ let ( mut int_digits , mut frac_digits ) = match parts[ 0 ] . find ( "." ) {
4959 Some ( i) => {
5060 // Cover special case .X and -.X where we behave as if there was a leading 0:
5161 // 0.X, -0.X.
52- match i {
62+ let int_digits = match i {
5363 0 => 1 ,
5464 1 if parts[ 0 ] . starts_with ( "-" ) => 2 ,
5565 _ => i,
56- }
66+ } ;
67+
68+ ( int_digits, parts[ 0 ] . len ( ) - i - 1 )
5769 }
58- None => parts[ 0 ] . len ( ) ,
70+ None => ( parts[ 0 ] . len ( ) , 0 ) ,
5971 } ;
6072
6173 // If there is an exponent, reparse that (yes this is not optimal,
6274 // but we can't necessarily exactly recover that from the parsed number).
6375 if parts. len ( ) == 2 {
6476 let exp = parts[ 1 ] . parse :: < i64 > ( ) . unwrap_or ( 0 ) ;
6577 // For positive exponents, effectively expand the number. Ignore negative exponents.
66- // Also ignore overflowed exponents (default 0 above ).
78+ // Also ignore overflowed exponents (unwrap_or(0) ).
6779 if exp > 0 {
68- digits + exp as usize
80+ int_digits += exp. try_into ( ) . unwrap_or ( 0 )
81+ } ;
82+ frac_digits = if exp < frac_digits as i64 {
83+ // Subtract from i128 to avoid any overflow
84+ ( frac_digits as i128 - exp as i128 ) . try_into ( ) . unwrap_or ( 0 )
6985 } else {
70- digits
86+ 0
7187 }
72- } else {
73- digits
88+ }
89+
90+ PreciseNumber {
91+ number : ebd,
92+ num_integral_digits : int_digits,
93+ num_fractional_digits : Some ( frac_digits) ,
7494 }
7595}
7696
@@ -80,36 +100,29 @@ impl FromStr for PreciseNumber {
80100 type Err = ParseNumberError ;
81101 fn from_str ( input : & str ) -> Result < Self , Self :: Err > {
82102 let ebd = match ExtendedBigDecimal :: extended_parse ( input) {
83- Ok ( ebd) => ebd,
103+ Ok ( ebd) => match ebd {
104+ // Handle special values
105+ ExtendedBigDecimal :: BigDecimal ( _) | ExtendedBigDecimal :: MinusZero => {
106+ // TODO: GNU `seq` treats small numbers < 1e-4950 as 0, we could do the same
107+ // to avoid printing senselessly small numbers.
108+ ebd
109+ }
110+ ExtendedBigDecimal :: Infinity | ExtendedBigDecimal :: MinusInfinity => {
111+ return Ok ( PreciseNumber {
112+ number : ebd,
113+ num_integral_digits : 0 ,
114+ num_fractional_digits : Some ( 0 ) ,
115+ } ) ;
116+ }
117+ ExtendedBigDecimal :: Nan | ExtendedBigDecimal :: MinusNan => {
118+ return Err ( ParseNumberError :: Nan ) ;
119+ }
120+ } ,
84121 Err ( ExtendedParserError :: Underflow ( ebd) ) => ebd, // Treat underflow as 0
85122 Err ( _) => return Err ( ParseNumberError :: Float ) ,
86123 } ;
87124
88- // Handle special values, get a BigDecimal to help digit-counting.
89- let bd = match ebd {
90- ExtendedBigDecimal :: Infinity | ExtendedBigDecimal :: MinusInfinity => {
91- return Ok ( PreciseNumber {
92- number : ebd,
93- num_integral_digits : 0 ,
94- num_fractional_digits : Some ( 0 ) ,
95- } ) ;
96- }
97- ExtendedBigDecimal :: Nan | ExtendedBigDecimal :: MinusNan => {
98- return Err ( ParseNumberError :: Nan ) ;
99- }
100- ExtendedBigDecimal :: BigDecimal ( ref bd) => {
101- // TODO: `seq` treats small numbers < 1e-4950 as 0, we could do the same
102- // to avoid printing senselessly small numbers.
103- bd. clone ( )
104- }
105- ExtendedBigDecimal :: MinusZero => BigDecimal :: zero ( ) ,
106- } ;
107-
108- Ok ( PreciseNumber {
109- number : ebd,
110- num_integral_digits : compute_num_integral_digits ( input, & bd) ,
111- num_fractional_digits : hexadecimalfloat:: parse_precision ( input) ,
112- } )
125+ Ok ( compute_num_digits ( input, ebd) )
113126 }
114127}
115128
0 commit comments