Почему преобразование целочисленной строки в float и double дает разные результаты? - PullRequest
0 голосов
/ 12 июля 2020
let n = "77777777"
let i = Int(n)!
let f = Float(n)!
let d = Double(n)!

print(i)
print(f)
print(d)
print(Int(f))
print(Int(d))

производит:

77777777

7.777778e + 07

77777777.0

77777776

77777777

Почему? Использование Xcode 11, Swift 5. Этот вопрос выглядит похожим, но начинается с десятичного числа, а не целого. Кроме того, здесь действительно нет математики с плавающей запятой.

Ответы [ 2 ]

1 голос
/ 12 июля 2020

Числа хранятся в конечной памяти. Независимо от того, выполняете ли вы арифметику с плавающей запятой c, вам нужен способ кодирования десятичного числа в двоичной памяти. Пока у вас ограниченная память (то есть всегда в реальном мире), вы должны выбрать, потратить свои биты на высокий диапазон или высокую точность, или на какой-то компромисс между ними.

7 цифр вводят вас в первую «область» компромисса Float. Вы можете «сделать это», но есть компромисс: при такой большой величине вы теряете некоторую точность. В данном случае целые числа округляются до ближайшего 10.

Float - это число с плавающим указателем одинарной точности IEEE 754. Его первая зона «компромисса» находится на уровне 16,777,217. От 0 до 16,777,216 можно точно представить каждое целое число. После этого не хватит точности, чтобы указать число до 2^0 (единицы, иначе единицы). Следующее, что лучше всего - это представить его правильно вплоть до ближайших 2^1 (двоек).

Проверьте это:

import Foundation

for originalInt in 16_777_210 ... 16_777_227 {
    let interMediateFloat = Float(originalInt)
    let backAsInt = Int(interMediateFloat)
    print("\(originalInt) -> \(backAsInt)")
}

print("\n...\n")

for originalInt in 33_554_430 ... 33_554_443 {
    let interMediateFloat = Float(originalInt)
    let backAsInt = Int(interMediateFloat)
    print("\(originalInt) -> \(backAsInt)")
}

печатает:

16777210 -> 16777210
16777211 -> 16777211
16777212 -> 16777212
16777213 -> 16777213
16777214 -> 16777214
16777215 -> 16777215
16777216 -> 16777216 // Last precisely representable whole number
16777217 -> 16777216 // rounds down
16777218 -> 16777218 // starts skipping by 2s
16777219 -> 16777220
16777220 -> 16777220
16777221 -> 16777220
16777222 -> 16777222
16777223 -> 16777224
16777224 -> 16777224
16777225 -> 16777224
16777226 -> 16777226
16777227 -> 16777228

...

33554430 -> 33554430
33554431 -> 33554432
33554432 -> 33554432
33554433 -> 33554432
33554434 -> 33554432 // Last whole number representable to the closest "two"
33554435 -> 33554436 // rounds up
33554436 -> 33554436
33554437 -> 33554436 // starts skipping by 4s
33554438 -> 33554440
33554439 -> 33554440
33554440 -> 33554440
33554441 -> 33554440
33554442 -> 33554440
33554443 -> 33554444

И так далее. По мере того, как величина становится больше, целые числа представляются со все меньшей и меньшей точностью. В крайнем случае, наибольшее целочисленное значение (340,282,346,638,528,859,811,704,183,484,516,925,440) и второе наибольшее целочисленное значение (340,282,326,356,119,256,160,033,759,537,265,639,424) отличаются на 20,282,409,603,651,670,423,947,251,286,016 (2^104).

Возможность express такого высокие числа происходят именно за счет неспособности точно хранить многие числа около этой величины. Округление происходит. В качестве альтернативы, двоичные целые числа, такие как Swift Int, имеют идеальную точность для целых чисел (всегда сохраняются с точностью до правильных единиц / единиц), но платят за это значительно меньшим максимальным размером (только 2,147,483,647 для подписанного Int32).

1 голос
/ 12 июля 2020

Хорошо, посмотрите на конвертер с плавающей запятой https://www.h-schmidt.net/FloatConverter/IEEE754.html. Он показывает вам биты, сохраненные при вводе числа в двоичном и шестнадцатеричном представлении, а также выдает ошибку из-за преобразования. Проблема в том, как это число представлено в стандарте. В случае с плавающей запятой ошибка действительно оказывается равной -1.

Фактически, любое число в диапазоне от 77777772 до 77777780 дает вам 77777776 как внутреннее представление мантиссы.

...