Skip to content

fix(arrow-cast): do not truncate integers when casting to Decimal32/64 - #10707

Open
yongster wants to merge 1 commit into
apache:mainfrom
yongster:fix/cast-integer-to-decimal32-truncation
Open

fix(arrow-cast): do not truncate integers when casting to Decimal32/64#10707
yongster wants to merge 1 commit into
apache:mainfrom
yongster:fix/cast-integer-to-decimal32-truncation

Conversation

@yongster

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

cast / cast_with_options from an integer array to Decimal32 or
Decimal64 can rewrite the value instead of rejecting it.

cast_integer_to_decimal first used AsPrimitive (as), which wraps when
the source integer does not fit the decimal native type. The precision check
then ran on the already-truncated value. If that wrapped value happened to
fit the requested precision, it was stored as if it were the original number.

Examples on current main:

  • Int64(5_000_000_000) -> Decimal32(9, 0) (safe: true) becomes 705032704
  • UInt32(4_000_000_000) -> Decimal32(9, 0) (safe: false) becomes -294967296
  • UInt64::MAX -> Decimal64(18, 0) (safe: false) becomes -1

The same inputs cast to Decimal128 already return null / Err, because
i64 as i128 is lossless.

What changes are included in this PR?

  • Convert the source integer through a range-checked i128 path
    (num_cast + DecimalCast) before applying scale and precision.
  • safe: true writes null when the original value does not fit; safe: false
    returns Err and reports the original value.
  • Add regression tests for Int64 -> Decimal32, UInt32 -> Decimal32, and
    UInt64::MAX -> Decimal64.

Are these changes tested?

Yes. I ran:

cargo test -p arrow-cast --lib -- cast
cargo clippy -p arrow-cast --all-targets --all-features -- -D warnings
cargo +stable fmt --all -- --check

Closes apache#10706.

cast_integer_to_decimal used AsPrimitive before the precision check.
That silently wraps values that do not fit the decimal native type, so
Int64(5_000_000_000) became Decimal32 705032704, and UInt32(4e9) /
UInt64::MAX could become negative.

Convert through a range-checked i128 path first, then apply scale and
precision. Safe casts become null; unsafe casts error with the original
value.

Add regression tests for Int64 -> Decimal32, UInt32 -> Decimal32, and
UInt64::MAX -> Decimal64.
@github-actions github-actions Bot added arrow Changes to the arrow crate arrow-cast labels Aug 16, 2026
@Jefffrey Jefffrey added the bug label Aug 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arrow Changes to the arrow crate arrow-cast bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cast from integer to Decimal32/64 silently truncates values that do not fit

2 participants