Swift 4.1.3 repairs Decimal arithmetic on Linux
Swift 4.1.3 brings a Decimal correction to Linux users on the 4.1 branch. The patch repairs carry and borrow handling and avoids floating-point conversion when constructing decimals from integers.
Adding 5538 and 2880.4 should produce 8418.4. A reported Linux Foundation defect instead produced 1864.8, with both Decimal addition and the corresponding NSDecimalNumber operation affected. This is an arithmetic implementation error, far larger than an ordinary disagreement about the last represented digit.
The July 30 Swift 4.1.3 announcement identifies a Linux bug-fix release addressing this Decimal issue. The important change is availability on the 4.1 release branch. A correction in newer development code does not help an application that still needs a maintained 4.1 toolchain.
The May patch by Simon Evans shows the mechanism. Addition needs to preserve a carry that remains after processing the operand words. Subtraction needs correct borrow computation and propagation. The patch also changes integer construction so that it no longer passes through Double.
I like Swift's explicit types, but choosing a decimal type only establishes the intended representation. Its arithmetic implementation still has to satisfy that representation's rules. This release matters because a type commonly selected for decimal calculations was returning a substantially incorrect value for ordinary operands.
The missing carry changes the magnitude
The implementation stores the mantissa in words and combines them through wider intermediate values. In the addition helper, each step adds corresponding words and any incoming carry. The low portion becomes a result word, while the high portion becomes the carry for the next step.
A final carry remains significant after the original operand words are exhausted. It represents another word in the result. The patch writes that word and updates the result length before checking whether the computed length exceeds Decimal's supported size.
This detail distinguishes a correct arithmetic operation from an operation that merely adds each existing pair of words. The result may need more storage than either original mantissa. Handling the intermediate capacity and the final supported length consistently is part of preserving the value.
The release's motivating case makes that failure observable through the public API. An application does not need to access internal words or construct an extreme value to encounter it. Ordinary decimal addition reaches the internal arithmetic helpers.
Subtraction needs a propagated borrow
Subtraction has the corresponding dependency between neighboring words. The patch computes a difference using a wider unsigned value, including the incoming borrow. It then determines whether a borrow was actually needed and carries that information into subsequent words.
The added tests include subtracting a smaller value from a number with a nonzero higher word and zero lower words. Those cases require a borrow to travel through the representation. Testing only operands whose corresponding words can subtract independently would miss this behavior.
The tests also cover reversed operand order for the reported decimal values. Positive and negative results exercise different paths around the arithmetic helpers. A fix for one reported addition therefore receives evidence from related subtraction cases instead of stopping at the original expression.
The patch's later test additions include arithmetic near the representable limits. They check an overflowing sum and carefully constructed mantissas around a high bit. These are targeted tests of word boundaries and result size, which are the implementation details the correction changes.
Constructing an integer should retain its value
The same patch removes a Double conversion from the UInt64 initializer. The replacement constructs the decimal representation directly. It removes trailing decimal zeros into the exponent and populates the mantissa words from the remaining integer.
For signed input, the new implementation handles the magnitude and sign separately. It explicitly accounts for the minimum signed integer, whose positive magnitude cannot fit in the same signed type. That case needs a path that does not attempt an invalid signed absolute value.
This part of the change concerns the value before addition or subtraction begins. A correct arithmetic helper cannot recover information lost while constructing an operand. Direct integer construction avoids that intermediate floating-point representation.
My preference for predictable value behavior extends to these conversions. An integer-to-decimal initializer should preserve the supplied integer within the decimal representation's capabilities. The implementation change addresses that expectation at the construction boundary.
The regression example should isolate decimal arithmetic
The final patch revises several tests to construct values from strings. That makes the expected decimal value explicit and avoids using a floating-point calculation as the reference result. The following reproducer follows that approach.
import Foundation
let left = Decimal(string: "5538")!
let right = Decimal(string: "2880.4")!
let expected = Decimal(string: "8418.4")!
assert(left + right == expected)
This assertion checks the documented arithmetic relationship directly. It gives the operands and the expected result separate decimal constructions. Its purpose is to expose the reported defect and verify the corrected operation, rather than compare two paths that could share an unrelated conversion problem.
The July discussion about the Linux defect explains why the maintenance release was needed. On July 27, Nicole Jacque said the fix had been cherry-picked to the 4.1 branch and snapshots were being generated. On July 30 she confirmed the snapshot was posted.
That sequence matters for users deciding whether a patch is available. A selected backport and a downloadable toolchain are different stages of delivery. Swift 4.1.3 supplies the latter for this Linux correction, allowing applications on the 4.1 branch to obtain the repaired Foundation arithmetic without depending on an unreleased language update.