В данном конкретном случае это потому, что .09 и .999999 не могут быть представлены с точной двоичной точностью (аналогично, 1/3 не может быть представлена с точной десятичной точностью). Например, 0,111111111111111111101111 основание 2 - это 0,9999998986721038818359375 основание 10. При добавлении 1 к предыдущему двоичному значению 0,11111111111111111111 основание 2 - это 0,9999904632568359375 основание 10. Нет точно двоичного значения для 0,9999999. Точность с плавающей точкой также ограничена пространством, отведенным для хранения показателя и дробной части мантиссы. Также, как и целочисленные типы, плавающая точка может выходить за пределы диапазона, хотя диапазон больше целочисленных диапазонов.
Запуск этого бита кода C ++ в отладчике Xcode,
float myFloat = 0.1;
показывает, что myFloat получает значение 0.100000001. Выключено на 0,000000001. Не много, но если в вычислении есть несколько арифметических операций, неточность может быть усугублена.
imho, очень хорошее объяснение с плавающей точкой - в главе 14 Введение в организацию компьютеров с языком ассемблера x86-64 и GNU / Linux Бобом Планцем из Калифорнийского государственного университета в Сономе (в отставке) http://bob.cs.sonoma.edu/getting_book.html. Следующее основано на этой главе.
Плавающая точка похожа на научную запись, где значение хранится в виде смешанного числа, большего или равного 1,0 и меньшего, чем 2,0 (мантисса), умноженное на другое число до некоторой степени (экспоненты). С плавающей точкой используется база 2, а не база 10, но в простой модели, которую дает Плантз, для ясности он использует базу 10. Представьте себе систему, в которой две позиции хранения используются для мантиссы, одна позиция используется для знака показателя степени * (0 представляет + и 1 представляет -), а одна позиция используется для показателя степени. Теперь добавьте 0,93 и 0,91. Ответ 1,8, а не 1,84.
9311 представляет 0,93, или 9,3, умноженное на 10 к -1.
9111 соответствует 0,91 или 9,1 от 10 до -1.
Точный ответ - 1,84, или 1,84, умноженный на 10 к 0, что было бы 18400, если бы у нас было 5 позиций, но, имея только четыре позиции, ответ - 1800, или 1,8, умноженное на 10 на ноль, или 1,8. Конечно, типы данных с плавающей запятой могут использовать более четырех позиций хранения, но количество позиций все еще ограничено.
Мало того, что точность ограничена пространством, но и «точное представление дробных значений в двоичном виде ограничено суммами обратных степеней двух». (Plantz, op. Cit.).
0,11100110 (двоичный) = 0,89843750 (десятичный)
0,11100111 (двоичный) = 0,90234375 (десятичный)
Нет точного представления 0,9 десятичного числа в двоичном виде. Даже выведение дроби из большего количества мест не работает, так как вы повторяете 1100 навсегда справа.
Начинающие программисты часто видят в арифметике с плавающей точкой больше
точнее, чем целое число. Это правда, что даже добавление двух очень больших
целые числа могут вызвать переполнение. Умножение делает это еще более вероятным
что результат будет очень большим и, таким образом, переполнением. И когда используется
с двумя целыми числами оператор / в C / C ++ вызывает дробную часть
потеряться. Тем не менее, ... представления с плавающей точкой имеют свои собственные
множество неточностей. (Plantz, op. Cit.)
* В плавающей запятой представлены как знак числа, так и знак показателя степени.