проблемы в сравнении с плавающей запятой

void main() { float f = 0.98; if(f <= 0.98) printf("hi"); else printf("hello"); getch(); } 

Я получаю эту проблему здесь. Используя разные значения с плавающей запятой, получаю разные результаты. Почему это происходит?

f использует точность float , но 0.98 по умолчанию имеет double точность, поэтому оператор f <= 0.98 сравнивается с использованием double точности.

Таким образом, f сравнивается с double , но может сделать результат немного большим, чем 0,98.

использование

 if(f <= 0.98f) 

или вместо этого используйте double for f .


Подробно ... предполагая, что float - это IEEE с одной точностью, а double - двойная точность IEEE .

Эти типы чисел с плавающей запятой сохраняются с представлением base-2. В base-2 это число нуждается в бесконечной точности для представления, поскольку это повторяющееся десятичное число:

 0.98 = 0.1111101011100001010001111010111000010100011110101110000101000... 

float может хранить только 24 бита значимых цифр, т. Е.

  0.111110101110000101000111_101... ^ round off here = 0.111110101110000101001000 = 16441672 / 2^24 = 0.98000001907... 

double может хранить 53 бит знаковых цифр, поэтому

  0.11111010111000010100011110101110000101000111101011100_00101000... ^ round off here = 0.11111010111000010100011110101110000101000111101011100 = 8827055269646172 / 2^53 = 0.97999999999999998224... 

Таким образом, 0,98 станет немного больше float и меньше в double .

Это связано с тем, что значения с плавающей запятой не являются точными представлениями числа. Все базовые десять номеров должны быть представлены на компьютере в качестве базовых чисел. Именно в этом преобразовании точность теряется.

Подробнее об этом читайте на http://en.wikipedia.org/wiki/Floating_point


Пример (от встречи с этой проблемой в мои дни VB6)

Чтобы преобразовать число 1.1 в одноточечное число с плавающей запятой, нам нужно преобразовать его в двоичный. Необходимо создать 32 бита.

Бит 1 – это знаковый бит (отрицательный [1] или позиция [0]). Биты 2-9 приведены для значения экспоненты. Биты 10-32 для мантиссы (также известный как коэффициент научной нотации)

Таким образом, для 1.1 одиночное значение с плавающей запятой сохраняется следующим образом (это усеченное значение, компилятор может округлить наименее значимый бит за кулисами, но все, что я делаю, обрезает его, что немного менее точно, но не изменяет результаты этого примера):

 s --exp--- -------mantissa-------- 0 01111111 00011001100110011001100 

Если вы заметили, что в мантиссе есть повторяющийся шаблон 0011. 1/10 в двоичном формате равно 1/3 в десятичном значении. Это продолжается вечно. Таким образом, чтобы получить значения из 32-битного значения с плавающей запятой с одиночной точностью, мы должны сначала преобразовать показатель экспоненты и мантиссы в десятичные числа, чтобы мы могли их использовать.

sign = 0 = положительное число

Показатель: 01111111 = 127

Мантисса: 00011001100110011001100 = 838860

С мантиссой нам нужно преобразовать ее в десятичное значение. Причина в том, что перед двоичным номером подразумевается целое число (например, 1.00011001100110011001100). Подразумеваемое число состоит в том, что мантисса представляет нормированное значение, которое должно использоваться в научной нотации: 1.0001100110011 …. * 2 ^ (x-127).

Чтобы получить десятичное значение из 838860, мы просто делим на 2 ^ -23, так как в мантиссе 23 бита. Это дает нам 0.099999904632568359375. Добавьте подразумеваемый 1 к мантиссе, дает нам 1.099999904632568359375. Показатель равен 127, но формула требует 2 ^ (x-127).

Итак, вот математика:

(1 + 099999904632568359375) * 2 ^ (127-127)

1.099999904632568359375 * 1 = 1.099999904632568359375

Как вы можете видеть, 1.1 не хранится в одном значении с плавающей запятой как 1.1.

  • Почему Double.MIN_VALUE не является отрицательным?
  • Как обнаружить переполнение и переполнение двойной точки с двойной точностью?
  • Почему преобразование из float в double изменяет значение?
  • Какой тип данных MySQL следует использовать для широты / долготы с 8 десятичными знаками?
  • Почему я вижу двойную переменную, инициализированную некоторым значением, например 21.4, как 21.399999618530273?
  • Сравнение значений с плавающей запятой
  • Сравнение с плавающей точкой
  • Как сравнить два числа с плавающей запятой в Bash?
  • Приведение float в int (побитовое) в C
  • Преобразование с двойным преобразованием без научной нотации
  • Java двойное сравнение epsilon
  • Interesting Posts

    Не удалось загрузить SOS в WinDbg

    Выборочно отключить предупреждения GCC только для части единицы перевода?

    строка printf, элемент переменной длины

    Комбинация клавиш для адресной строки проводника Windows

    Обновление Windows 8.1 1 Использование диска 100%

    Как открыть приложение при запуске X session

    Возможно ли, чтобы SCP с удаленного на локальный, вошедший в пульт и не зная IP-адрес локального компьютера?

    Печать правильного количества десятичных точек с помощью cout

    Избегайте регистрационного окна Google Chrome?

    Как избежать раскладки клавиатуры, автоматически меняющейся на окнах

    Запретить ViewPager уничтожать заставку

    Как показать прогресс «Истории файлов» в Windows8?

    Есть ли способ получить идентификатор электронной почты пользователя после проверки его / ее идентификатора Twitter с помощью OAuth?

    Всякий раз, когда Google Chrome открыт, мой жесткий диск очень быстро вращается

    «JSON» – неопределенная ошибка только в IE

    Давайте будем гением компьютера.