Почему двойники печатаются по-разному в словарях?
let dic : [Double : Double] = [1.1 : 2.3, 2.3 : 1.1, 1.2 : 2.3]
print(dic)// [2.2999999999999998: 1.1000000000000001, 1.2: 2.2999999999999998, 1.1000000000000001: 2.2999999999999998]
let double : Double = 2.3
let anotherdouble : Double = 1.1
print(double) // 2.3
print(anotherdouble) // 1.1
Я не понимаю, почему значения для компилятора печатают из словарей по-разному?
Я на Swift 3, Xcode 8. Является ли это ошибкой или каким-то странным способом оптимизации материала или чего-то еще?
ИЗМЕНИТЬ
Что еще более странно:
Некоторые ценности переходят, некоторые идут ниже, некоторые остаются такими, какие они есть! 1,1 меньше 1,1000000000000001, тогда как 2,3 составляет более 2,2999999999999998, 1,2 составляет всего 1,2
Ответы
Ответ 1
Как уже упоминалось в комментариях, Double
не может хранить
значение 1.1
точно. Swift использует (как и многие другие языки)
двоичные числа с плавающей запятой в соответствии с IEEE 754
стандарт.
Ближайшее число до 1.1
, которое может быть представлено как Double
,
1.100000000000000088817841970012523233890533447265625
и ближайшим числом до 2.3
, которое может быть представлено как Double
, является
2.29999999999999982236431605997495353221893310546875
Печать этого числа означает, что он преобразуется в строку с
десятичное представление снова, и это делается с разными
точность, в зависимости от того, как вы печатаете номер.
Из исходного кода HashedCollections.swift.gyb видно, что метод description
Dictionary
использует debugPrint()
для обоих ключей и значений,
и debugPrint(x)
выводит значение x.debugDescription
(если x
соответствует CustomDebugStringConvertible
).
С другой стороны, print(x)
вызывает x.description
, если x
соответствует
до CustomStringConvertible
.
Итак, вы видите другой вывод description
и debugDescription
Double
:
print(1.1.description) // 1.1
print(1.1.debugDescription) // 1.1000000000000001
Из исходного кода Swift можно увидеть
что оба используют swift_floatingPointToString()
в Stubs.cpp, с параметром Debug
, установленным на false
и true
, соответственно.
Этот параметр управляет точностью преобразования числа в строку:
int Precision = std::numeric_limits<T>::digits10;
if (Debug) {
Precision = std::numeric_limits<T>::max_digits10;
}
Значение этих констант см. в std:: numeric_limits:
-
digits10
- количество десятичных цифр, которое может быть представлено без изменений,
-
max_digits10
- количество десятичных цифр, необходимых для дифференциации всех значений этого типа.
Итак, description
создает строку с меньшими десятичными цифрами. Что
строка может быть преобразована в Double
и обратно в строку, дающую
тот же результат.
debugDescription
создает строку с более десятичными цифрами, так что
любые два разных значения с плавающей запятой будут выдавать другой результат.
Ответ 2
Да, Swift использует двоичные числа с плавающей запятой, сохраняя его в словаре
Используйте словарь как [Двойной: Любой], используйте Float, если ваш номер 32 бит, а затем преобразован в AnyObject
См. ниже пример
let strDecimalNumber = "8.37"
var myDictionary : [String: Any] = [:]
myDictionary["key1"] = Float(strDecimalNumber) as AnyObject // 8.369999999999999
myDictionary["key2"] = Double(strDecimalNumber) as AnyObject //8.369999999999999
myDictionary["key3"] = Double(8.37) as AnyObject //8.369999999999999
myDictionary["key4"] = Float(8.37) as AnyObject //8.37
myDictionary["key5"] = 8.37 // 8.3699999999999992
myDictionary["key6"] = strDecimalNumber // "8.37" it is String
myDictionary["key7"] = strDecimalNumber.description // "8.37" it is String
myDictionary["key8"] = Float(10000000.01) // 10000000.0
myDictionary["key9"] = Float(100000000.01) // 100000000.0
myDictionary["key10"] = Float(1000000000.01) // 1e+09
myDictionary["key11"] = Double(1000000000.01) // 1000000000.01
print(myDictionary)
myDictionary будет напечатан как
[ "key1": 8.37, "key2": 8.369999999999999, "key3": 8.369999999999999, "key4": 8.37, "key5": 8.3699999999999992, "key6": "8.37", "key7": "8.37", "key8": 10000000.0, "key9": 100000000.0, "key10": 1e + 09, "key11": 1000000000.01]
Как упоминалось Мартином R в вышеприведенном ответе с использованием .description будет рассматриваться как String not actual Float