@@ -9,6 +9,7 @@ use uucore::display::Quotable;
99use uucore:: i18n:: decimal:: locale_grouping_separator;
1010use uucore:: translate;
1111
12+ use crate :: numeric:: ParsedNumber ;
1213use crate :: options:: { NumfmtOptions , RoundMethod , TransformOptions } ;
1314use crate :: units:: {
1415 DisplayableSuffix , RawSuffix , Result , Suffix , Unit , iec_bases_f64, si_bases_f64,
@@ -147,12 +148,17 @@ fn detailed_error_message(s: &str, unit: Unit, unit_separator: &str) -> Option<S
147148 None
148149}
149150
150- fn parse_number_part ( s : & str , input : & str ) -> Result < f64 > {
151+ fn parse_number_part ( s : & str , input : & str ) -> Result < ParsedNumber > {
151152 if s. ends_with ( '.' ) {
152153 return Err ( translate ! ( "numfmt-error-invalid-number" , "input" => input. quote( ) ) ) ;
153154 }
154155
156+ if let Ok ( n) = s. parse :: < i128 > ( ) {
157+ return Ok ( ParsedNumber :: ExactInt ( n) ) ;
158+ }
159+
155160 s. parse :: < f64 > ( )
161+ . map ( ParsedNumber :: Float )
156162 . map_err ( |_| translate ! ( "numfmt-error-invalid-number" , "input" => input. quote( ) ) )
157163}
158164
@@ -161,7 +167,7 @@ fn parse_suffix(
161167 unit : Unit ,
162168 unit_separator : & str ,
163169 explicit_unit_separator : bool ,
164- ) -> Result < ( f64 , Option < Suffix > ) > {
170+ ) -> Result < ( ParsedNumber , Option < Suffix > ) > {
165171 let trimmed = s. trim_end ( ) ;
166172 if trimmed. is_empty ( ) {
167173 return Err ( translate ! ( "numfmt-error-invalid-number-empty" ) ) ;
@@ -364,7 +370,22 @@ fn remove_suffix(i: f64, s: Option<Suffix>, u: Unit) -> Result<f64> {
364370 }
365371}
366372
367- fn transform_from ( s : & str , opts : & TransformOptions , options : & NumfmtOptions ) -> Result < f64 > {
373+ fn try_scale_exact_int_with_from_unit (
374+ value : ParsedNumber ,
375+ from_unit : usize ,
376+ ) -> Option < ParsedNumber > {
377+ let integer = value. exact_int ( ) ?;
378+ let from_unit = i128:: try_from ( from_unit) . ok ( ) ?;
379+ let scaled = integer. checked_mul ( from_unit) ?;
380+
381+ Some ( ParsedNumber :: ExactInt ( scaled) )
382+ }
383+
384+ fn transform_from (
385+ s : & str ,
386+ opts : & TransformOptions ,
387+ options : & NumfmtOptions ,
388+ ) -> Result < ParsedNumber > {
368389 let ( i, suffix) = parse_suffix (
369390 s,
370391 opts. from ,
@@ -375,17 +396,24 @@ fn transform_from(s: &str, opts: &TransformOptions, options: &NumfmtOptions) ->
375396 detailed_error_message ( s, opts. from , & options. unit_separator ) . unwrap_or ( original)
376397 } ) ?;
377398 let had_no_suffix = suffix. is_none ( ) ;
378- let i = i * ( opts. from_unit as f64 ) ;
399+
400+ if had_no_suffix {
401+ if let Some ( scaled) = try_scale_exact_int_with_from_unit ( i, opts. from_unit ) {
402+ return Ok ( scaled) ;
403+ }
404+ }
405+
406+ let i = i. to_f64 ( ) * ( opts. from_unit as f64 ) ;
379407
380408 remove_suffix ( i, suffix, opts. from ) . map ( |n| {
381- // GNU numfmt doesn't round values if no --from argument is provided by the user
382- if opts. from == Unit :: None || had_no_suffix {
409+ let n = if opts. from == Unit :: None || had_no_suffix {
383410 if n == -0.0 { 0.0 } else { n }
384411 } else if n < 0.0 {
385412 -n. abs ( ) . ceil ( )
386413 } else {
387414 n. ceil ( )
388- }
415+ } ;
416+ ParsedNumber :: Float ( n)
389417 } )
390418}
391419
@@ -480,13 +508,43 @@ fn consider_suffix(
480508 }
481509}
482510
511+ fn try_format_exact_int_without_suffix_scaling (
512+ value : ParsedNumber ,
513+ opts : & TransformOptions ,
514+ precision : usize ,
515+ ) -> Option < String > {
516+ if opts. to != Unit :: None {
517+ return None ;
518+ }
519+
520+ let integer = value. exact_int ( ) ?;
521+ let to_unit = i128:: try_from ( opts. to_unit ) . ok ( ) ?;
522+
523+ if integer % to_unit != 0 {
524+ return None ;
525+ }
526+
527+ let scaled = integer / to_unit;
528+
529+ Some ( if precision == 0 {
530+ scaled. to_string ( )
531+ } else {
532+ format ! ( "{scaled}.{}" , "0" . repeat( precision) )
533+ } )
534+ }
535+
483536fn transform_to (
484- s : f64 ,
537+ s : ParsedNumber ,
485538 opts : & TransformOptions ,
486539 round_method : RoundMethod ,
487540 precision : usize ,
488541 unit_separator : & str ,
489542) -> Result < String > {
543+ if let Some ( result) = try_format_exact_int_without_suffix_scaling ( s, opts, precision) {
544+ return Ok ( result) ;
545+ }
546+
547+ let s = s. to_f64 ( ) ;
490548 let i2 = s / ( opts. to_unit as f64 ) ;
491549 let ( i2, s) = consider_suffix ( i2, opts. to , round_method, precision) ?;
492550 Ok ( match s {
@@ -771,7 +829,7 @@ mod tests {
771829 let result = parse_suffix ( "1Q" , Unit :: Auto , "" , false ) ;
772830 assert ! ( result. is_ok( ) ) ;
773831 let ( number, suffix) = result. unwrap ( ) ;
774- assert_eq ! ( number, 1.0 ) ;
832+ assert_eq ! ( number. to_f64 ( ) , 1.0 ) ;
775833 assert ! ( suffix. is_some( ) ) ;
776834 let ( raw_suffix, with_i) = suffix. unwrap ( ) ;
777835 assert_eq ! ( raw_suffix as i32 , RawSuffix :: Q as i32 ) ;
@@ -780,7 +838,7 @@ mod tests {
780838 let result = parse_suffix ( "2R" , Unit :: Auto , "" , false ) ;
781839 assert ! ( result. is_ok( ) ) ;
782840 let ( number, suffix) = result. unwrap ( ) ;
783- assert_eq ! ( number, 2.0 ) ;
841+ assert_eq ! ( number. to_f64 ( ) , 2.0 ) ;
784842 assert ! ( suffix. is_some( ) ) ;
785843 let ( raw_suffix, with_i) = suffix. unwrap ( ) ;
786844 assert_eq ! ( raw_suffix as i32 , RawSuffix :: R as i32 ) ;
@@ -789,7 +847,7 @@ mod tests {
789847 let result = parse_suffix ( "3k" , Unit :: Auto , "" , false ) ;
790848 assert ! ( result. is_ok( ) ) ;
791849 let ( number, suffix) = result. unwrap ( ) ;
792- assert_eq ! ( number, 3.0 ) ;
850+ assert_eq ! ( number. to_f64 ( ) , 3.0 ) ;
793851 assert ! ( suffix. is_some( ) ) ;
794852 let ( raw_suffix, with_i) = suffix. unwrap ( ) ;
795853 assert_eq ! ( raw_suffix as i32 , RawSuffix :: K as i32 ) ;
@@ -798,7 +856,7 @@ mod tests {
798856 let result = parse_suffix ( "4Qi" , Unit :: Auto , "" , false ) ;
799857 assert ! ( result. is_ok( ) ) ;
800858 let ( number, suffix) = result. unwrap ( ) ;
801- assert_eq ! ( number, 4.0 ) ;
859+ assert_eq ! ( number. to_f64 ( ) , 4.0 ) ;
802860 assert ! ( suffix. is_some( ) ) ;
803861 let ( raw_suffix, with_i) = suffix. unwrap ( ) ;
804862 assert_eq ! ( raw_suffix as i32 , RawSuffix :: Q as i32 ) ;
@@ -807,7 +865,7 @@ mod tests {
807865 let result = parse_suffix ( "5Ri" , Unit :: Auto , "" , false ) ;
808866 assert ! ( result. is_ok( ) ) ;
809867 let ( number, suffix) = result. unwrap ( ) ;
810- assert_eq ! ( number, 5.0 ) ;
868+ assert_eq ! ( number. to_f64 ( ) , 5.0 ) ;
811869 assert ! ( suffix. is_some( ) ) ;
812870 let ( raw_suffix, with_i) = suffix. unwrap ( ) ;
813871 assert_eq ! ( raw_suffix as i32 , RawSuffix :: R as i32 ) ;
@@ -1023,9 +1081,9 @@ mod tests {
10231081
10241082 #[ test]
10251083 fn test_parse_number_part_valid ( ) {
1026- assert_eq ! ( parse_number_part( "42" , "42" ) . unwrap( ) , 42.0 ) ;
1027- assert_eq ! ( parse_number_part( "-3.5" , "-3.5" ) . unwrap( ) , -3.5 ) ;
1028- assert_eq ! ( parse_number_part( "0" , "0" ) . unwrap( ) , 0.0 ) ;
1084+ assert_eq ! ( parse_number_part( "42" , "42" ) . unwrap( ) . to_f64 ( ) , 42.0 ) ;
1085+ assert_eq ! ( parse_number_part( "-3.5" , "-3.5" ) . unwrap( ) . to_f64 ( ) , -3.5 ) ;
1086+ assert_eq ! ( parse_number_part( "0" , "0" ) . unwrap( ) . to_f64 ( ) , 0.0 ) ;
10291087 }
10301088
10311089 #[ test]
@@ -1094,15 +1152,21 @@ mod tests {
10941152 #[ test]
10951153 fn test_parse_number_part_large_and_tiny ( ) {
10961154 assert_eq ! (
1097- parse_number_part( "999999999999" , "999999999999" ) . unwrap( ) ,
1155+ parse_number_part( "999999999999" , "999999999999" )
1156+ . unwrap( )
1157+ . to_f64( ) ,
10981158 999_999_999_999.0
10991159 ) ;
11001160 assert_eq ! (
1101- parse_number_part( "0.000000001" , "0.000000001" ) . unwrap( ) ,
1161+ parse_number_part( "0.000000001" , "0.000000001" )
1162+ . unwrap( )
1163+ . to_f64( ) ,
11021164 0.000_000_001
11031165 ) ;
11041166 assert_eq ! (
1105- parse_number_part( "-99999999" , "-99999999" ) . unwrap( ) ,
1167+ parse_number_part( "-99999999" , "-99999999" )
1168+ . unwrap( )
1169+ . to_f64( ) ,
11061170 -99_999_999.0
11071171 ) ;
11081172 }
0 commit comments