@@ -855,6 +855,8 @@ fn parse_e_notation<T: DecimalType>(
855
855
result. div_wrapping ( base. pow_wrapping ( -exp. add_wrapping ( 1 ) as _ ) ) ;
856
856
let rounding_digit =
857
857
result_with_one_scale_up. sub_wrapping ( result_with_scale. mul_wrapping ( base) ) ;
858
+ //rounding digit is the next digit after result with scale, it helps in rounding to nearest integer
859
+ // with scale 1 rounding digit for 247e-2 is 7, hence result is 2.5, whereas rounding digit for 244e-2 is 4, hence result is 2.4
858
860
if rounding_digit >= T :: Native :: usize_as ( 5 ) {
859
861
result = result_with_scale. add_wrapping ( T :: Native :: usize_as ( 1 ) ) ;
860
862
} else {
@@ -875,8 +877,8 @@ pub fn parse_decimal<T: DecimalType>(
875
877
scale : i8 ,
876
878
) -> Result < T :: Native , ArrowError > {
877
879
let mut result = T :: Native :: usize_as ( 0 ) ;
878
- let mut fractionals: i8 = 0 ;
879
- let mut digits: u8 = 0 ;
880
+ let mut fractionals: i16 = 0 ;
881
+ let mut digits: u16 = 0 ;
880
882
let mut rounding_digit = -1 ; // to store digit after the scale for rounding
881
883
let base = T :: Native :: usize_as ( 10 ) ;
882
884
@@ -907,7 +909,7 @@ pub fn parse_decimal<T: DecimalType>(
907
909
// Ignore leading zeros.
908
910
continue ;
909
911
}
910
- if fractionals == scale && scale != 0 {
912
+ if fractionals == scale as i16 && scale != 0 {
911
913
// Capture the rounding digit once
912
914
if rounding_digit < 0 {
913
915
rounding_digit = ( b - b'0' ) as i8 ;
@@ -926,8 +928,8 @@ pub fn parse_decimal<T: DecimalType>(
926
928
if * b == b'e' || * b == b'E' {
927
929
result = parse_e_notation :: < T > (
928
930
s,
929
- digits as u16 ,
930
- fractionals as i16 ,
931
+ digits,
932
+ fractionals,
931
933
result,
932
934
point_index,
933
935
precision as u16 ,
@@ -942,7 +944,7 @@ pub fn parse_decimal<T: DecimalType>(
942
944
"can't parse the string value {s} to decimal"
943
945
) ) ) ;
944
946
}
945
- if fractionals == scale {
947
+ if fractionals == scale as i16 {
946
948
// Capture the rounding digit once
947
949
if rounding_digit < 0 {
948
950
rounding_digit = ( b - b'0' ) as i8 ;
@@ -974,8 +976,8 @@ pub fn parse_decimal<T: DecimalType>(
974
976
b'e' | b'E' => {
975
977
result = parse_e_notation :: < T > (
976
978
s,
977
- digits as u16 ,
978
- fractionals as i16 ,
979
+ digits,
980
+ fractionals,
979
981
result,
980
982
index,
981
983
precision as u16 ,
@@ -995,24 +997,25 @@ pub fn parse_decimal<T: DecimalType>(
995
997
}
996
998
997
999
if !is_e_notation {
998
- if fractionals < scale {
999
- let exp = scale - fractionals;
1000
- if exp as u8 + digits > precision {
1000
+ if fractionals < scale as i16 {
1001
+ let exp = scale as i16 - fractionals;
1002
+ if exp + digits as i16 > precision as i16 {
1001
1003
return Err ( ArrowError :: ParseError ( format ! (
1002
1004
"parse decimal overflow ({s})"
1003
1005
) ) ) ;
1004
1006
}
1005
1007
let mul = base. pow_wrapping ( exp as _ ) ;
1006
1008
result = result. mul_wrapping ( mul) ;
1007
- } else if digits > precision {
1009
+ } else if digits > precision as u16 {
1008
1010
return Err ( ArrowError :: ParseError ( format ! (
1009
1011
"parse decimal overflow ({s})"
1010
1012
) ) ) ;
1011
1013
}
1012
1014
if scale == 0 {
1013
1015
result = result. div_wrapping ( base. pow_wrapping ( fractionals as u32 ) )
1014
1016
}
1015
- //add one if >=5
1017
+ //rounding digit is the next digit after result with scale, it is used to do rounding to nearest integer
1018
+ // with scale 1 rounding digit for 2.47 is 7, hence result is 2.5, whereas rounding digit for 2.44 is 4,hence result is 2.4
1016
1019
if rounding_digit >= 5 {
1017
1020
result = result. add_wrapping ( T :: Native :: usize_as ( 1 ) ) ;
1018
1021
}
@@ -2652,6 +2655,7 @@ mod tests {
2652
2655
assert_eq ! ( result_256_e. unwrap( ) , result_256_d. unwrap( ) ) ;
2653
2656
}
2654
2657
2658
+ // here the 2nd column is expected result, it is also converted using parse_decimal
2655
2659
let test_rounding_for_e_notation_varying_scale = [
2656
2660
( "1.2345e4" , "12345" , 2 ) ,
2657
2661
( "12345e-5" , "0.12" , 2 ) ,
@@ -2846,6 +2850,55 @@ mod tests {
2846
2850
}
2847
2851
}
2848
2852
2853
+ #[ test]
2854
+ fn test_parse_decimal_rounding ( ) {
2855
+ let test_rounding_for_e_notation_varying_scale = [
2856
+ ( "1.2345e4" , "12345" , 2 ) ,
2857
+ ( "12345e-5" , "0.12" , 2 ) ,
2858
+ ( "12345E-5" , "0.123" , 3 ) ,
2859
+ ( "12345e-5" , "0.1235" , 4 ) ,
2860
+ ( "1265E-4" , ".127" , 3 ) ,
2861
+ ( "12.345e3" , "12345.000" , 3 ) ,
2862
+ ( "1.2345e4" , "12345" , 0 ) ,
2863
+ ( "1.2345e3" , "1235" , 0 ) ,
2864
+ ( "1.23e-3" , "0" , 0 ) ,
2865
+ ( "123e-2" , "1" , 0 ) ,
2866
+ ( "-1e-15" , "-0.0000000000" , 10 ) ,
2867
+ ( "1e-15" , "0.0000000000" , 10 ) ,
2868
+ ( "1e15" , "1000000000000000" , 2 ) ,
2869
+ ] ;
2870
+
2871
+ for ( e, d, scale) in test_rounding_for_e_notation_varying_scale {
2872
+ let result_128_e = parse_decimal :: < Decimal128Type > ( e, 38 , scale) ;
2873
+ let result_128_d = parse_decimal :: < Decimal128Type > ( d, 38 , scale) ;
2874
+ assert_eq ! ( result_128_e. unwrap( ) , result_128_d. unwrap( ) ) ;
2875
+ let result_256_e = parse_decimal :: < Decimal256Type > ( e, 38 , scale) ;
2876
+ let result_256_d = parse_decimal :: < Decimal256Type > ( d, 38 , scale) ;
2877
+ assert_eq ! ( result_256_e. unwrap( ) , result_256_d. unwrap( ) ) ;
2878
+ }
2879
+
2880
+ let edge_tests_256_error = [
2881
+ ( & f64:: INFINITY . to_string ( ) , 0 ) ,
2882
+ ( & f64:: NEG_INFINITY . to_string ( ) , 0 ) ,
2883
+ ] ;
2884
+ for ( s, scale) in edge_tests_256_error {
2885
+ let result = parse_decimal :: < Decimal256Type > ( s, 76 , scale) ;
2886
+ assert_eq ! (
2887
+ format!( "Parser error: can't parse the string value {s} to decimal" ) ,
2888
+ result. unwrap_err( ) . to_string( )
2889
+ ) ;
2890
+ }
2891
+
2892
+ let edge_tests_256_overflow = [ ( & f64:: MIN . to_string ( ) , 0 ) , ( & f64:: MAX . to_string ( ) , 0 ) ] ;
2893
+ for ( s, scale) in edge_tests_256_overflow {
2894
+ let result = parse_decimal :: < Decimal256Type > ( s, 76 , scale) ;
2895
+ assert_eq ! (
2896
+ format!( "Parser error: parse decimal overflow ({s})" ) ,
2897
+ result. unwrap_err( ) . to_string( )
2898
+ ) ;
2899
+ }
2900
+ }
2901
+
2849
2902
#[ test]
2850
2903
fn test_parse_empty ( ) {
2851
2904
assert_eq ! ( Int32Type :: parse( "" ) , None ) ;
0 commit comments