How to initialize Decimal?

Both the behavior of Decimal initializers from Double and JSON decoding fall under Foundation, and the choice of maintaining backwards compatibility with existing code versus aligning with Swift string representation is up to Apple-internal processes.

Swift language support for decimal floating-point literals is tracked by the bugs listed above by @Jens and is for sure a key improvement that will need to be made in this area.

7 Likes

It addresses some of the shortcomings but it will still be limited by Double (or the way Swift converts doubles to and from strings), ie:

let a = PreciseDecimal(  1234567890.0123456789 )
let b = Decimal(string: "1234567890.0123456789")!
print(a) // 1234567890.0123458
print(b) // 1234567890.0123456789
3 Likes

That’s fair. I should probably add a disclaimer about that. It covers lots of use cases but falls short on very high precision numbers that Double simply can’t represent. We won’t get true 1:1 literal precision until Apple addresses it themselves.

2 Likes

Currently, FloatLiteralConvertible has another problem for this kind of use: It supports hexfloats, which also cause complications for decimal-based formats.

Because of this, I would prefer to see new protocols for "DecimalFloatLiteralConvertible" and "HexadecimalFloatLiteralConvertible" that stores the literal in a lossless form. (Maybe "InfiniteFloatLiteralConvertible" and "NanFloatLiteralConvertible" to support all possible FP literals?) There's a fair number of details to work out to make this both performant for standard types and flexible enough for arbitrary-precision constants.

2 Likes

It's simpler than you think, actually. Basically, you just need a couple of extra bits precision to identify the two "midpoints", values that are exactly halfway between your initial value and the next higher/lower Doubles. You then convert both of those midpoints to text at the same time, stopping at the first digit that differs.

In your example, the midpoint above your value starts with "3.133..." and the midpoint below it starts with "3.132...", so 3.133 is the shortest decimal that converts to exactly your value.

Most interestingly, this can be done very quickly. Because these "short, round-trip-correct" values have a known limit on their size, the arithmetic can be aggressively optimized, unlike a more general formatting routine that can produce any number of digits.

1 Like