Сравнение чисел с плавающей запятой Python

Я просто просматриваю некоторые основы Python и есть сложная проблема сравнения чисел с плавающей запятой.

2.2 * 3.0 == 6.6
3.3 * 2.0 == 6.6

Я думал, что они оба вернут False. Однако второй дал мне Истину. enter image description here

Пожалуйста, помогите мне здесь. Спасибо!

Ответ 1

Это может быть освещено:

>>> float.hex(2.2 * 3.0)
'0x1.a666666666667p+2'
>>> float.hex(3.3 * 2.0)
'0x1.a666666666666p+2'
>>> float.hex(6.6)
'0x1.a666666666666p+2'

Хотя все они отображаются в десятичном формате как 6.6, когда вы проверяете внутреннее представление, два из них представлены одинаково, в то время как один из них не является.

Ответ 2

Чтобы закончить хороший ответ Амадана, вот более очевидный способ увидеть это 2.2 * 3. и 3.3 * 2. не представлены одним и тем же float: в оболочке Python

>>> 2.2 * 3.
6.6000000000000005
>>> 3.3 * 2.
6.6

На самом деле оболочка Python отображает представление чисел, которое по определению должно позволять правильному построению соответствующего поплавка из представления, поэтому вы видите численное приближение 2.2 * 3, которое делает Python. Тот факт, что 2.2 * 3.!= 3,3 * 2. очевидно, когда видны все необходимые цифры, например, выше.

Ответ 3

Также очевидно, что 3.3 * 2.0 по численности идентична 6.6. Последнее вычисление является не чем иным, как увеличением двоичного показателя степени, поскольку оно является результатом умножения со степенью двойки. Вы можете увидеть это в следующем:

    | s exponent    significant
----+-------------------------------------------------------------------
1.1 | 0 01111111111 0001100110011001100110011001100110011001100110011010
2.2 | 0 10000000000 0001100110011001100110011001100110011001100110011010
3.3 | 0 10000000000 1010011001100110011001100110011001100110011001100110
6.6 | 0 10000000001 1010011001100110011001100110011001100110011001100110

Выше вы видите двоичное представление чисел с плавающей точкой 3.3 и 6.6. Единственная разница в двух числах - это показатель степени, поскольку они умножаются только на два. Мы знаем, что IEEE-754 будет:

  • приблизить десятичное число с наименьшей числовой ошибкой
  • может представлять все целые числа до 2^53 точно (для двоичного 64)

Так как 2.0 точно представимо, умножение с этим числом будет не более чем изменением показателя степени. Таким образом, все последующее создаст одинаковые числа с плавающей точкой:

6.6 == 0.825 * 16.0 == 1.65 * 4.0 == 3.3*2.0 == 13.2 * 0.5 == ...

Означает ли это, что 2.2*3.0 отличается от 6.6 из-за значительного? Нет, это просто из-за ошибок округления при умножении.

Пример, где это работало бы, было бы 5.5*2.0 == 2.2*5.0 == 11.0. Здесь округление было благоприятным