Почему двойники печатаются по-разному в словарях?

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