Search code examples
iosswiftoverflownsnumberformatter

NSNumberFormatter.string(from: ) maximum possible Value exceeded does not result in Overflow - Swift


I am converting numbers to pure English words, and I ran into some very odd situations: NSNumberFormatter has a strange output, smaller than the desired result, but the number taken as parameter doesn't cause an overflow.

I have the following code:

 import Foundation
 var numberFormatter: NumberFormatter = NumberFormatter()
 numberFormatter.numberStyle = .spellOut
 var result: String?
 result = numberFormatter.string(from: 999999999999999999)
 print(result ?? "nil")

and this prints eighteen quadrillion fourteen trillion three hundred ninety-eight billion five hundred nine million four hundred eighty-one thousand nine hundred eighty-four, which is the equivalent of 18014398509481984< 999999999999999999. If I try to get the words from 18014398509481984, the result is the one I expected, the string described above. However, if I add one more 9 to 999.., it crashes with the message:

integer literal 9999999999999999999 overflows when stored into Int

Here is a Swift Sandbox Test, in order to make the question more understandable.


My actual question is: Assuming that the output of the first try: 180140398509481984 is some kind of limit for numberFormatter.string(from:), why does 999999999999999999 not result in Overflow, but just displays that limit, and 9999999999999999999 (with an extra 9) results in Overflow?


Solution

  • The 9_999_999_999_999_999_999 causes an Int overflow because it is larger than Int64.max is 9_223_372_036_854_775_807 (i.e. 0x7fffffffffffffff).

    Regarding why number formatter is capping out at 18_014_398_509_481_984 (i.e., 254, 0x40000000000000) for .spelledOut, that seems suspiciously like a bug stemming from 64-bit floating point representions of the value. We can't be sure without going through the source for NSNumberFormatter and NSNumber in some detail, but I suggest this because the upper ceiling here is, coincidentally, precisely double the largest integer value that a 64-bit floating point type can capture faithfully.