Decimal Add/Sub kernels#8724
Open
mhk197 wants to merge 5 commits into
Open
Conversation
Mechanical move mirroring the compare split: dtype-generic lane machinery stays in numeric/mod.rs (bounds relaxed to T: Default so wider decimal types can reuse it), primitive-specific code moves to numeric/primitive.rs, tests to numeric/tests.rs. Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Add and Sub over decimal arrays sharing a decimal dtype, applied to the unscaled stored integers (exact at a shared scale) in a working width wide enough that in-precision inputs cannot spuriously overflow. Precision violations error only on valid lanes, matching primitive semantics. Mul and Div need rescaling and are gated off until follow-up PRs. widened_buffer moves to arrays/decimal so compare and numeric share it. Signed-off-by: Matt Katz <mhkatz97@gmail.com>
The conformance harness now accepts decimal arrays, checking Add/Sub results element-wise against DecimalScalar::checked_binary_numeric for representative constants. Wire it into the decimal, chunked, and decimal-byte-parts compute tests, and add decimal Add cases to the binary_ops benchmark. Signed-off-by: Matt Katz <mhkatz97@gmail.com>
This was referenced Jul 11, 2026
Merging this PR will not alter performance
Performance Changes
Comparing Footnotes
|
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds shared infrastructure for decimal arithmetic and native Add/Sub kernels.
Operand alignment
The execution kernel requires both operands to have the same precision and scale (nullability may differ). Addition and subtraction can then operate directly and exactly on the unscaled integers because both values use the same power-of-ten factor.
Vortex's optional coercion pass can align operands with different decimal dtypes before execution. Query-engine integrations may instead insert their own planner casts. If neither layer aligns the operands, the kernel rejects them rather than performing an implicit per-kernel rescale.
Result precision
Adding or subtracting two
p-digit integers can require one additional carry digit. This PR follows the Arrow/Hive result-type rule for already aligned operands, capped at Vortex's maximum decimal precision:The scale remains unchanged because the operation is performed on integers at the same scale. For example:
Widening the logical precision can also widen the native storage used for the result. The kernel executes at the smallest native width capable of representing the result dtype, including the important
decimal(18, s) -> decimal(19, s)(i64 -> i128) anddecimal(38, s) -> decimal(39, s)(i128 -> i256) transitions. Inputs may already use a wider physical representation; they are converted into the selected working width before arithmetic.Why precision 76 is the limit
Vortex's widest decimal storage is a signed 256-bit integer. Its positive limit is:
That is enough to represent every 76-digit decimal integer, whose largest value is
10^76 - 1, but not every 77-digit integer, whose largest value would be10^77 - 1. Some 77-digit values do fit ini256; that is not sufficient for adecimal(77, s)type, because a declared precision must support the type's entire range. Therefore the largest Vortex decimal dtype isdecimal(76, s).At precision 76 the result dtype cannot widen to precision 77, so it stays at precision 76 and the kernel explicitly checks the mathematical result against the precision-76 bound. Let
M = 10^76 - 1:This is a logical decimal-precision check, not merely an
i256overflow check. In fact, evenM + Mstill fits physically ini256, but it is outside the declared precision-76 range and must be rejected. Computing ini256first and then checking the decimal bound cleanly distinguishes the physical representation from the logical dtype contract.Nulls, constants, and errors
Implementation
numeric/{mod,primitive,decimal,tests}.rs, mirroring the compare implementation.execute::<DecimalArray>, while extracting constant and null operands directly.widened_bufferfromarrays/decimalbetween comparison and numeric execution.Coverage
The conformance suite exercises Decimal, Chunked, and DecimalByteParts arrays, including:
binary_opsalso includes non-nulli64and nullablei128decimal Add benchmarks.