MCPcopy Create free account
hub / github.com/apache/datafusion / round_decimal

Function round_decimal

datafusion/spark/src/function/math/round.rs:317–370  ·  view source on GitHub ↗

Round a decimal value represented as its unscaled integer using HALF_UP rounding mode (ties round away from zero). This matches Spark's `RoundBase` behaviour for `DecimalType`, which calls `BigDecimal.setScale(scale, RoundingMode.HALF_UP)`. Decimals are stored as `(unscaled_value, precision, scale)` where the real value equals `unscaled_value * 10^(-scale)`. This function operates on the unscal

(
    value: V,
    input_scale: i8,
    decimal_places: i32,
)

Source from the content-addressed store, hash-verified

315/// round_decimal(2345_i128, 3, 2) → Ok(2350)
316/// ```
317fn round_decimal<V: ArrowNativeTypeOp>(
318 value: V,
319 input_scale: i8,
320 decimal_places: i32,
321) -> Result<V> {
322 let diff = i64::from(input_scale) - i64::from(decimal_places);
323 if diff <= 0 {
324 // Nothing to round – the requested precision is finer than (or equal to) the
325 // stored scale.
326 return Ok(value);
327 }
328
329 let diff = diff as u32;
330
331 let one = V::ONE;
332 let two = V::from_usize(2).ok_or_else(|| {
333 (exec_err!("Internal error: could not create constant 2") as Result<(), _>)
334 .unwrap_err()
335 })?;
336 let ten = V::from_usize(10).ok_or_else(|| {
337 (exec_err!("Internal error: could not create constant 10") as Result<(), _>)
338 .unwrap_err()
339 })?;
340
341 let Ok(factor) = ten.pow_checked(diff) else {
342 // 10^diff overflows the decimal type — the rounding position is beyond
343 // the representable range, so any value rounds to 0.
344 // This matches Spark's BigDecimal.setScale behavior where rounding to a
345 // scale far beyond the number's magnitude yields 0.
346 return Ok(V::ZERO);
347 };
348
349 let mut quotient = value.div_wrapping(factor);
350 let remainder = value.mod_wrapping(factor);
351
352 // HALF_UP: round away from zero when remainder is exactly half
353 let threshold = factor.div_wrapping(two);
354 if remainder >= threshold {
355 quotient = quotient.add_checked(one).map_err(|_| {
356 (exec_err!("Overflow while rounding decimal") as Result<(), _>).unwrap_err()
357 })?;
358 } else if remainder <= threshold.neg_wrapping() {
359 quotient = quotient.sub_checked(one).map_err(|_| {
360 (exec_err!("Overflow while rounding decimal") as Result<(), _>).unwrap_err()
361 })?;
362 }
363
364 // Re-scale the quotient back to `input_scale` so the returned unscaled integer is
365 // at the original scale. `factor` is already `10^diff` which is exactly the shift
366 // we need.
367 quotient.mul_checked(factor).map_err(|_| {
368 (exec_err!("Overflow while rounding decimal") as Result<(), _>).unwrap_err()
369 })
370}
371
372// ---------------------------------------------------------------------------
373// Macros for array dispatch

Callers 1

spark_roundFunction · 0.70

Calls 4

unwrap_errMethod · 0.80
add_checkedMethod · 0.80
sub_checkedMethod · 0.80
mul_checkedMethod · 0.80

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…