Skip to content

Commit 136c0a6

Browse files
committed
add more tests and convert digits to u16
fix the type of precision and scale fix the clippy errors
1 parent 8b81039 commit 136c0a6

File tree

1 file changed

+66
-13
lines changed

1 file changed

+66
-13
lines changed

arrow-cast/src/parse.rs

+66-13
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)