Подтвердить что ты не робот

0.1 float больше 0,1 double. Я ожидал, что это будет ложным

Пусть:

double d = 0.1;
float f = 0.1;

следует выражение

(f > d)

return true или false?

Эмпирически, ответ true. Однако я ожидал, что это будет false.

Поскольку 0.1 не может быть идеально представлен в двоичном формате, а double имеет от 15 до 16 десятичные цифры точности, а float имеет только 7. Таким образом, оба они меньше 0.1, а double ближе к 0.1.

Мне нужно точное описание true.

4b9b3361

Ответ 1

Я бы сказал, что ответ зависит от режима округления при преобразовании double в float. float имеет 24 бинарных бита точности, а double - 53. В двоичном выражении 0,1:

0.1₁₀ = 0.0001100110011001100110011001100110011001100110011…₂
             ^        ^         ^   ^ 
             1       10        20  24

Итак, если мы округлимся на 24-й цифре, мы получим

0.1₁₀ ~ 0.000110011001100110011001101

что больше точного значения и более точного приближения на 53 цифры.

Ответ 2

Число 0,1 будет округлено до ближайшего числа с плавающей запятой с заданной точностью. Это приближение может быть либо больше, либо меньше 0,1, поэтому, не глядя на фактические значения, вы не можете предсказать, является ли приближение с одной точностью или двойной точностью.

Здесь значение двойной точности округляется до (с использованием интерпретатора Python):

>>> "%.55f" % 0.1
'0.1000000000000000055511151231257827021181583404541015625'

И здесь значение одиночной точности:

>>> "%.55f" % numpy.float32("0.1")
'0.1000000014901161193847656250000000000000000000000000000'

Итак, вы можете видеть, что приближение с одной точностью больше.

Ответ 3

Если вы конвертируете .1 в двоичный код, вы получаете:

0.000110011001100110011001100110011001100110011001100...

повторяется навсегда

Сопоставляя типы данных, вы получаете:

float(.1)  = %.00011001100110011001101
                                     ^--- note rounding
double(.1) = %.0001100110011001100110011001100110011001100110011010

Преобразуйте это в базу 10:

float(.1)  = .10000002384185791015625
double(.1) = .100000000000000088817841970012523233890533447265625

Это было взято из статьи, написанной Брюсом Доусоном. его можно найти здесь:
Дубли не являются плавающими, поэтому не сравнивайте их

Ответ 4

Я думаю, комментарий Эрика Липперта по этому вопросу на самом деле является самым ясным объяснением, поэтому я отвечу ему как ответ:

Предположим, вы вычисляете 1/9 в 3-значном десятичном и 6-значном десятичном значении. 0,111 0.111111, правильно?

Теперь предположим, что вы вычисляете 6/9. 0,667 > 0,6666667, правильно?

У вас не может быть этого, что 6/9 в трехзначном десятичном значении равно 0.666, потому что это не самый близкий 3-значный десятичный символ до 6/9!

Ответ 5

Поскольку он не может быть точно представлен, сравнение 1/10 в базе 2 похоже на сравнение 1/7 в базе 10.

1/7 = 0.142857142857... но сравнивая при разных базовых 10 точках (3 против 6 знаков после запятой), мы имеем 0.143 > 0.142857.

Ответ 6

Просто добавьте к другим ответам, говорящим о IEEE-754 и x86: проблема еще сложнее, чем кажется. Не существует "одного" представления 0,1 в IEEE-754 - их два. Было бы справедливо либо округление последней цифры вниз, либо вверх. Эта разница может и действительно возникает, поскольку x86 не использует 64-битные вычисления для своих внутренних вычислений с плавающей запятой; он фактически использует 80 бит! Это называется двойной расширенной точностью.

Таким образом, даже среди просто компиляторов x86 иногда бывает, что одно и то же число представлено двумя разными способами, потому что некоторые вычисляют его двоичное представление с 64-битами, в то время как другие используют 80.


На самом деле это может произойти даже с одним и тем же компилятором даже на одной машине!

#include <iostream>
#include <cmath>

void foo(double x, double y)
{
  if (std::cos(x) != std::cos(y)) {
    std::cout << "Huh?!?\n";  //← you might end up here when x == y!!
  }
}

int main()
{
  foo(1.0, 1.0);
  return 0;
}

См. Почему cos(x) != cos(y), хотя x == y? для получения дополнительной информации.

Ответ 7

Ранг double больше, чем для float в преобразованиях. Проводя логическое сравнение, f приводится к удвоению, и, возможно, реализация, которую вы используете, дает непоследовательные результаты. Если вы суффикс f, поэтому компилятор регистрирует его как float, тогда вы получаете 0.00, который является ложным в двойном типе. Непроверенные плавающие типы являются двойными.

#include <stdio.h>
#include <float.h>

int main()
{
     double d = 0.1;
     float f = 0.1f;
     printf("%f\n", (f > d));

     return 0;
}