Skip to content

[ABA-27] fix(vortex-array): direct integer Decimal-to-i64 cast (no f64 precision loss)#3

Open
abnobdoss wants to merge 2 commits into
developfrom
fix/aba-27-decimal-to-i64-precision
Open

[ABA-27] fix(vortex-array): direct integer Decimal-to-i64 cast (no f64 precision loss)#3
abnobdoss wants to merge 2 commits into
developfrom
fix/aba-27-decimal-to-i64-precision

Conversation

@abnobdoss
Copy link
Copy Markdown
Owner

Summary

The decimal-to-i64 cast in DecimalScalar::cast routes the i128
coefficient through f64 before truncating to i64. Integer raw values
whose absolute magnitude exceeds 2^53 cannot be represented exactly in
IEEE-754 binary64, so the cast silently rounds such values before the
integer truncation — losing precision even when the result fits in i64.

This violates SQL exact-numeric cast semantics (SQL:2016 §6.13): a cast
between exact-numeric types must preserve the value when the source fits
in the target, or raise numeric_value_out_of_range — silently rounding
is not a permitted outcome.

This PR replaces the f64 intermediate in the PType::I64 branch with a
direct integer cast: divide by 10^scale on the i128 coefficient, then
range-check via i64::try_from. The change mirrors Apache Arrow's
cast_decimal_to_integer. Other primitive branches are intentionally
left unchanged; this PR fixes only the branch where the precision loss
has the largest practical impact (the narrower integer targets already
fit comfortably in the f64 mantissa).

The minimal probe value is 2^53 + 1 = 9_007_199_254_740_993: its nearest
f64 neighbour is 2^53 = 9_007_199_254_740_992 (round-to-even). On
develop, casting Decimal(38, 0) with raw value 2^53 + 1 to i64
returns 2^53. With this fix it returns 2^53 + 1.

Linear

Validation

Repro test added inline in
vortex-array/src/scalar/typed_view/decimal/tests.rs as
issue_aba27_decimal_to_i64_preserves_precision_above_2_53. Verified
the test fails on develop without the fix (returns 2^53 instead of
2^53 + 1) and passes with the fix.

Checks run locally:

  • cargo test -p vortex-array --lib — 2753 passed, 0 failed.
  • cargo clippy -p vortex-array --all-targets --all-features — clean.
  • cargo +nightly fmt --all — applied.

AI Assistance Disclosure

This PR was authored with assistance from Claude Code (Anthropic). The
underlying bug analysis, reproducer, and fix were reviewed by the human
contributor before submission.

Abanoub Doss and others added 2 commits May 21, 2026 07:58
… above 2^53

The decimal-to-primitive cast in `DecimalScalar::cast` routes the i128
coefficient through `f64` before truncating to i64. Integer raw values
above `2^53` cannot be represented exactly in `f64`, so they are silently
rounded before the integer cast — violating SQL exact-numeric cast
semantics for values that fit in i64.

This test exercises the smallest such value (2^53 + 1) cast from
Decimal(38, 0) to i64. It currently fails: the cast yields 2^53
instead of 2^53 + 1.

Signed-off-by: Abanob Doss <abnobdoss@proton.me>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Abanoub Doss <abanoub.doss@gmail.com>
…diate)

The decimal-to-i64 branch of `DecimalScalar::cast` previously computed
`actual_value = scaled_value as f64 / scale_factor as f64` and then did
`actual_value as i64` with an `f64` range check. Integer raw values whose
absolute magnitude exceeds `2^53` cannot be represented exactly in
IEEE-754 binary64, so the cast silently rounded such values before the
integer truncation — losing precision even when the result fits trivially
in i64.

Cast directly from the `i128` coefficient: divide by `10^scale` on the
integer to get the unscaled value, then range-check against `i64` via
`i64::try_from`. This mirrors Apache Arrow's `cast_decimal_to_integer`
and preserves SQL exact-numeric cast semantics.

Other primitive branches are intentionally unchanged; this commit fixes
only the i64 branch where the precision loss has the largest practical
impact (i32 and narrower targets already fit in the f64 mantissa).

Fixes ABA-27.

Signed-off-by: Abanob Doss <abnobdoss@proton.me>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Abanoub Doss <abanoub.doss@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant