Skip to content

Commit

Permalink
removed unused method parse_string_to_decimal_native and moved over a…
Browse files Browse the repository at this point in the history
…ll tests
  • Loading branch information
himadripal committed Dec 26, 2024
1 parent 460d323 commit a4f0667
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 231 deletions.
137 changes: 0 additions & 137 deletions arrow-cast/src/cast/decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,107 +231,6 @@ where
)?))
}

#[allow(dead_code)]
/// Parses given string to specified decimal native (i128/i256) based on given
/// scale. Returns an `Err` if it cannot parse given string.
pub(crate) fn parse_string_to_decimal_native<T: DecimalType>(
value_str: &str,
scale: usize,
) -> Result<T::Native, ArrowError>
where
T::Native: DecimalCast + ArrowNativeTypeOp,
{
let value_str = value_str.trim();
let parts: Vec<&str> = value_str.split('.').collect();
if parts.len() > 2 {
return Err(ArrowError::InvalidArgumentError(format!(
"Invalid decimal format: {value_str:?}"
)));
}

let (negative, first_part) = if parts[0].is_empty() {
(false, parts[0])
} else {
match parts[0].as_bytes()[0] {
b'-' => (true, &parts[0][1..]),
b'+' => (false, &parts[0][1..]),
_ => (false, parts[0]),
}
};

let integers = first_part;
let decimals = if parts.len() == 2 { parts[1] } else { "" };

if !integers.is_empty() && !integers.as_bytes()[0].is_ascii_digit() {
return Err(ArrowError::InvalidArgumentError(format!(
"Invalid decimal format: {value_str:?}"
)));
}

if !decimals.is_empty() && !decimals.as_bytes()[0].is_ascii_digit() {
return Err(ArrowError::InvalidArgumentError(format!(
"Invalid decimal format: {value_str:?}"
)));
}

// Adjust decimal based on scale
let mut number_decimals = if decimals.len() > scale {
let decimal_number = i256::from_string(decimals).ok_or_else(|| {
ArrowError::InvalidArgumentError(format!("Cannot parse decimal format: {value_str}"))
})?;

let div = i256::from_i128(10_i128).pow_checked((decimals.len() - scale) as u32)?;

let half = div.div_wrapping(i256::from_i128(2));
let half_neg = half.neg_wrapping();

let d = decimal_number.div_wrapping(div);
let r = decimal_number.mod_wrapping(div);

// Round result
let adjusted = match decimal_number >= i256::ZERO {
true if r >= half => d.add_wrapping(i256::ONE),
false if r <= half_neg => d.sub_wrapping(i256::ONE),
_ => d,
};

let integers = if !integers.is_empty() {
i256::from_string(integers)
.ok_or_else(|| {
ArrowError::InvalidArgumentError(format!(
"Cannot parse decimal format: {value_str}"
))
})
.map(|v| v.mul_wrapping(i256::from_i128(10_i128).pow_wrapping(scale as u32)))?
} else {
i256::ZERO
};

format!("{}", integers.add_wrapping(adjusted))
} else {
let padding = if scale > decimals.len() { scale } else { 0 };

let decimals = format!("{decimals:0<padding$}");
format!("{integers}{decimals}")
};

if negative {
number_decimals.insert(0, '-');
}

let value = i256::from_string(number_decimals.as_str()).ok_or_else(|| {
ArrowError::InvalidArgumentError(format!(
"Cannot convert {} to {}: Overflow",
value_str,
T::PREFIX
))
})?;

T::Native::from_decimal(value).ok_or_else(|| {
ArrowError::InvalidArgumentError(format!("Cannot convert {} to {}", value_str, T::PREFIX))
})
}

pub(crate) fn generic_string_to_decimal_cast<'a, T, S>(
from: &'a S,
precision: u8,
Expand Down Expand Up @@ -620,39 +519,3 @@ where
let array = array.unary::<_, T>(op);
Ok(Arc::new(array))
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_parse_string_to_decimal_native() -> Result<(), ArrowError> {
assert_eq!(
parse_string_to_decimal_native::<Decimal128Type>("123", 0)?,
123_i128
);
assert_eq!(
parse_string_to_decimal_native::<Decimal128Type>("123", 5)?,
12300000_i128
);

assert_eq!(
parse_string_to_decimal_native::<Decimal128Type>("123.45", 0)?,
123_i128
);
assert_eq!(
parse_string_to_decimal_native::<Decimal128Type>("123.45", 5)?,
12345000_i128
);

assert_eq!(
parse_string_to_decimal_native::<Decimal128Type>("123.4567891", 0)?,
123_i128
);
assert_eq!(
parse_string_to_decimal_native::<Decimal128Type>("123.4567891", 5)?,
12345679_i128
);
Ok(())
}
}
93 changes: 0 additions & 93 deletions arrow-cast/src/cast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8498,99 +8498,6 @@ mod tests {
);
}

#[test]
fn test_parse_string_to_decimal() {
assert_eq!(
Decimal128Type::format_decimal(
parse_string_to_decimal_native::<Decimal128Type>("123.45", 2).unwrap(),
38,
2,
),
"123.45"
);
assert_eq!(
Decimal128Type::format_decimal(
parse_string_to_decimal_native::<Decimal128Type>("12345", 2).unwrap(),
38,
2,
),
"12345.00"
);
assert_eq!(
Decimal128Type::format_decimal(
parse_string_to_decimal_native::<Decimal128Type>("0.12345", 2).unwrap(),
38,
2,
),
"0.12"
);
assert_eq!(
Decimal128Type::format_decimal(
parse_string_to_decimal_native::<Decimal128Type>(".12345", 2).unwrap(),
38,
2,
),
"0.12"
);
assert_eq!(
Decimal128Type::format_decimal(
parse_string_to_decimal_native::<Decimal128Type>(".1265", 2).unwrap(),
38,
2,
),
"0.13"
);
assert_eq!(
Decimal128Type::format_decimal(
parse_string_to_decimal_native::<Decimal128Type>(".1265", 2).unwrap(),
38,
2,
),
"0.13"
);

assert_eq!(
Decimal256Type::format_decimal(
parse_string_to_decimal_native::<Decimal256Type>("123.45", 3).unwrap(),
38,
3,
),
"123.450"
);
assert_eq!(
Decimal256Type::format_decimal(
parse_string_to_decimal_native::<Decimal256Type>("12345", 3).unwrap(),
38,
3,
),
"12345.000"
);
assert_eq!(
Decimal256Type::format_decimal(
parse_string_to_decimal_native::<Decimal256Type>("0.12345", 3).unwrap(),
38,
3,
),
"0.123"
);
assert_eq!(
Decimal256Type::format_decimal(
parse_string_to_decimal_native::<Decimal256Type>(".12345", 3).unwrap(),
38,
3,
),
"0.123"
);
assert_eq!(
Decimal256Type::format_decimal(
parse_string_to_decimal_native::<Decimal256Type>(".1265", 3).unwrap(),
38,
3,
),
"0.127"
);
}

fn test_cast_string_to_decimal(array: ArrayRef) {
// Decimal128
let output_type = DataType::Decimal128(38, 2);
Expand Down
24 changes: 23 additions & 1 deletion arrow-cast/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2573,8 +2573,9 @@ mod tests {
}

let tests_with_varying_scale = [
// ("123.4567891", 12345679_i128, 5),
("123.4567891", 12345679_i128, 5),
("123.4567891", 123_i128, 0),
("123.45", 12345000_i128, 5),
];
for (str, e, scale) in tests_with_varying_scale {
let result_128_a = parse_decimal::<Decimal128Type>(str, 20, scale);
Expand Down Expand Up @@ -2614,6 +2615,27 @@ mod tests {
let result_256_d = parse_decimal::<Decimal256Type>(d, 20, scale);
assert_eq!(result_256_e.unwrap(), result_256_d.unwrap());
}

let test_decimal_format_check = [
("123.45", "123.45", 2),
("12345", "12345", 2),
("0.12345", "0.12", 2),
(".12345", "0.12", 2),
("123.45", "123.450", 3),
("12345", "12345.000", 3),
("0.12345", "0.123", 3),
(".1265", ".127", 3),
];

for (e, d, scale) in test_decimal_format_check {
let result_128_e = parse_decimal::<Decimal128Type>(e, 38, scale);
let result_128_d = parse_decimal::<Decimal128Type>(d, 38, scale);
assert_eq!(result_128_e.unwrap(), result_128_d.unwrap());
let result_256_e = parse_decimal::<Decimal256Type>(e, 38, scale);
let result_256_d = parse_decimal::<Decimal256Type>(d, 38, scale);
assert_eq!(result_256_e.unwrap(), result_256_d.unwrap());
}

let can_not_parse_tests = [
"123,123",
".",
Expand Down

0 comments on commit a4f0667

Please sign in to comment.