NSNumberFormatter.string(from:) максимально возможное превышение значения не приводит к переполнению – Swift

Я конвертирую числа в чисто английские слова и столкнулся с очень странными ситуациями: NSNumberFormatter выдает странный результат, меньший, чем желаемый результат, но число, взятое в качестве параметра, не вызывает переполнения.

У меня есть следующий код:

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

и это напечатает eighteen quadrillion fourteen trillion three hundred ninety-eight billion five hundred nine million four hundred eighty-one thousand nine hundred eighty-four, что эквивалентно 18014398509481984999999999999999999. Если я попытаюсь получить слова из 18014398509481984, результат будет тем, который я ожидал, строкой, описанной выше. Однако, если я добавлю еще один 9 к 999.., он вылетает с сообщением:

целочисленный литерал 9999999999999999999 переполняется при сохранении в Int

Вот тест Swift Sandbox, чтобы сделать вопрос более понятно.


Мой фактический вопрос: Предполагая, что вывод первой попытки: 180140398509481984 является своего рода ограничением для numberFormatter.string(from:), почему 999999999999999999 не приводит к переполнению, а просто отображает этот предел, а 9999999999999999999 (с дополнительным 9) приводит к переполнению?


person Mr. Xcoder    schedule 04.02.2017    source источник
comment
Странный. Даже если вы используете 180140398509481985, вы все равно получите 180140398509481984 при написании.   -  person rmaddy    schedule 05.02.2017
comment
Тоже странный предел. Это между 2^57 и 2^58.   -  person rmaddy    schedule 05.02.2017
comment
@rmaddy это именно то, о чем я спрашиваю. Это какое-то ограничение только для этой функции или проблема с максимальным значением Int/NSNumber?   -  person Mr. Xcoder    schedule 05.02.2017
comment
По какой-то причине я не смог найти никакого особого свойства этого числа, я не знаю, что происходит.   -  person Mr. Xcoder    schedule 05.02.2017
comment
Похоже, это какое-то ограничение/ошибка стиля spellOut NumberFormatter.   -  person rmaddy    schedule 05.02.2017
comment
Да, это может быть ошибка, связанная с spellOut или string(from:)   -  person Mr. Xcoder    schedule 05.02.2017
comment
Давайте продолжим обсуждение в чате.   -  person Mr. Xcoder    schedule 05.02.2017
comment
Я только что запустил версию того же кода для macOS/Objective-C и получил тот же странный результат. Так что это не относится к iOS или Swift.   -  person rmaddy    schedule 05.02.2017
comment
@rmaddy - Даже если вы используете 180140398509481985, вы все равно получите 180140398509481984 ... Это тоже странный предел. Это между 2 ^ 57 и 2 ^ 58 ... ОП неправильно расшифровал число. Это 18_014_398_509_481_984, то есть 2^54.   -  person Rob    schedule 05.02.2017


Ответы (1)


9_999_999_999_999_999_999 вызывает переполнение Int, поскольку оно больше, чем Int64.max, равно 9_223_372_036_854_775_807 (т.е. 0x7fffffffffffffff).

Что касается того, почему средство форматирования чисел ограничивается 18_014_398_509_481_984 (т. е. 254, 0x40000000000000) для .spelledOut, это подозрительно похоже на ошибку, связанную с 64-битным представлением значения с плавающей запятой. Мы не можем быть уверены, не изучив источник для NSNumberFormatter и NSNumber в некоторых деталях, но я предлагаю это, потому что верхний предел здесь, по совпадению, точно удваивает самое большое целочисленное значение, которое может точно захватить 64-битный тип с плавающей запятой.

person Rob    schedule 05.02.2017
comment
Foundation и Core Foundation имеют открытый исходный код. Функция CFNumberFormatterCreateStringWithNumber даже есть всеобщее предупреждение о том, что значения CFNumbers с большими беззнаковыми 64-битными целыми числами не выдерживают этого. Похоже, Apple уже знала об этой проблеме. - person Code Different; 05.02.2017