in arrow-cast/src/cast.rs [2745:2824]
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 integers = parts[0].trim_start_matches('0');
let decimals = if parts.len() == 2 { parts[1] } else { "" };
// Adjust decimal based on scale
let 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}")
};
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
))
})
}