Puzzled by a crash in my application, on a line of code that looked reasonable, it became necessary to dig a little deeper in how some of my Decimal numbers are created:
let newValue = Decimal.init(binary.maskedUInt64Value)
After testing a series of different 64-bit integer values, I found this:
(lldb) po Decimal.init(UInt64.max - 1024)
▿ 18446744073709547520
▿ _mantissa : 8 elements
- .0 : 38912
- .1 : 39321
- .2 : 39321
- .3 : 6553
- .4 : 0
- .5 : 0
- .6 : 0
- .7 : 0
(lldb) po Decimal.init(UInt64.max - 1023)
error: warning: couldn't get required object pointer (substituting NULL): Couldn't load 'self' because its value couldn't be evaluated
error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
The process has been returned to the state before expression evaluation.
It turns out, any UInt64 value in the range from (UInt64.max - 1023) to UInt64.max crashes Decimal.init(value: UInt64). So, for the time being, I need to prevent values within that range. Here is a possible solution:
let intValue = binary.maskedUInt64Value
var newValue: Decimal
if intValue > UInt64.max - 1024 {
newValue = Decimal.init(UInt64.max - 1024) // larger values crash!
newValue.add(Decimal.init(intValue - (UInt64.max - 1024)))
} else {
newValue = Decimal.init(intValue)
}
Building on this, one can employ an extension with a custom initializer. In places where such very large numbers may occur, one could then use: Decimal.init(protected: binary.maskedUInt64Value).
extension Decimal {
init(protected value: UInt64) {
if value > UInt64.max - 1024 {
self.init(UInt64.max - 1024) // larger values crash!
self.add(Decimal.init(value - (UInt64.max - 1024)))
} else {
self.init(value)
}
}
}