@@ -855,6 +855,8 @@ fn parse_e_notation<T: DecimalType>(
855855 result. div_wrapping ( base. pow_wrapping ( -exp. add_wrapping ( 1 ) as _ ) ) ;
856856 let rounding_digit =
857857 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
858860 if rounding_digit >= T :: Native :: usize_as ( 5 ) {
859861 result = result_with_scale. add_wrapping ( T :: Native :: usize_as ( 1 ) ) ;
860862 } else {
@@ -875,8 +877,8 @@ pub fn parse_decimal<T: DecimalType>(
875877 scale : i8 ,
876878) -> Result < T :: Native , ArrowError > {
877879 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 ;
880882 let mut rounding_digit = -1 ; // to store digit after the scale for rounding
881883 let base = T :: Native :: usize_as ( 10 ) ;
882884
@@ -907,7 +909,7 @@ pub fn parse_decimal<T: DecimalType>(
907909 // Ignore leading zeros.
908910 continue ;
909911 }
910- if fractionals == scale && scale != 0 {
912+ if fractionals == scale as i16 && scale != 0 {
911913 // Capture the rounding digit once
912914 if rounding_digit < 0 {
913915 rounding_digit = ( b - b'0' ) as i8 ;
@@ -926,8 +928,8 @@ pub fn parse_decimal<T: DecimalType>(
926928 if * b == b'e' || * b == b'E' {
927929 result = parse_e_notation :: < T > (
928930 s,
929- digits as u16 ,
930- fractionals as i16 ,
931+ digits,
932+ fractionals,
931933 result,
932934 point_index,
933935 precision as u16 ,
@@ -942,7 +944,7 @@ pub fn parse_decimal<T: DecimalType>(
942944 "can't parse the string value {s} to decimal"
943945 ) ) ) ;
944946 }
945- if fractionals == scale {
947+ if fractionals == scale as i16 {
946948 // Capture the rounding digit once
947949 if rounding_digit < 0 {
948950 rounding_digit = ( b - b'0' ) as i8 ;
@@ -974,8 +976,8 @@ pub fn parse_decimal<T: DecimalType>(
974976 b'e' | b'E' => {
975977 result = parse_e_notation :: < T > (
976978 s,
977- digits as u16 ,
978- fractionals as i16 ,
979+ digits,
980+ fractionals,
979981 result,
980982 index,
981983 precision as u16 ,
@@ -995,24 +997,25 @@ pub fn parse_decimal<T: DecimalType>(
995997 }
996998
997999 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 {
10011003 return Err ( ArrowError :: ParseError ( format ! (
10021004 "parse decimal overflow ({s})"
10031005 ) ) ) ;
10041006 }
10051007 let mul = base. pow_wrapping ( exp as _ ) ;
10061008 result = result. mul_wrapping ( mul) ;
1007- } else if digits > precision {
1009+ } else if digits > precision as u16 {
10081010 return Err ( ArrowError :: ParseError ( format ! (
10091011 "parse decimal overflow ({s})"
10101012 ) ) ) ;
10111013 }
10121014 if scale == 0 {
10131015 result = result. div_wrapping ( base. pow_wrapping ( fractionals as u32 ) )
10141016 }
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
10161019 if rounding_digit >= 5 {
10171020 result = result. add_wrapping ( T :: Native :: usize_as ( 1 ) ) ;
10181021 }
@@ -2652,6 +2655,7 @@ mod tests {
26522655 assert_eq ! ( result_256_e. unwrap( ) , result_256_d. unwrap( ) ) ;
26532656 }
26542657
2658+ // here the 2nd column is expected result, it is also converted using parse_decimal
26552659 let test_rounding_for_e_notation_varying_scale = [
26562660 ( "1.2345e4" , "12345" , 2 ) ,
26572661 ( "12345e-5" , "0.12" , 2 ) ,
@@ -2846,6 +2850,55 @@ mod tests {
28462850 }
28472851 }
28482852
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+
28492902 #[ test]
28502903 fn test_parse_empty ( ) {
28512904 assert_eq ! ( Int32Type :: parse( "" ) , None ) ;
0 commit comments