2016-01-20 4 views
0

Я только что был через жука, где сравниваются два числа, и я нашел следующее интересное:Double и значение с плавающей точкой сравнения

assert 1 == 1;//true   
    assert 1d == 1;//true   
    assert 1 == 1f;//true   
    assert 1d == 1f;//true 
    assert 1.1 == 1.1;//true   
    assert 1.1d == 1.1;//true   
    assert 1.1 == 1.1f;//false   
    assert 1.1d == 1.1f;//false 

Мой вопрос: почему только два последних утверждения являются ложными?

+0

Очень хорошие объяснения: https://randomascii.wordpress.com/2012/06/26/doubles-are-not-floats- so-dont-compare-them/ –

+1

Вы ДОЛЖНЫ прочитать [Что должен знать каждый компьютерный ученый о арифметике с плавающей точкой] (https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) ,Это канонический ресурс для этого. –

+0

В чем смысл проверки равенства для двух разных типов значений ... неважно .. – haifzhan

ответ

13

Это из-за округления.

1 может быть представлено точно как двойной или поплавок, поэтому первые 4 сравнения работают так, как ожидалось.

1.1 == 1.1 сравнивает двойное с самим собой и работает должным образом.

1.1d == 1.1 точно такой же, как указано выше (d подразумевается в).

В последних двух сравнениях сравниваются 1.1 двойные по 1.1 поплавка. За исключением того, что 1.1 не является точно представимым как float (соответственно double), поэтому он округляется до ближайшего поплавка (соответственно двойного), но double имеет «более высокое разрешение», поэтому они не округляются одинаково.

Чтобы увидеть точные значения:

System.out.println(new BigDecimal(1.1f)); //1.10000002384185791015625 
System.out.println(new BigDecimal(1.1d)); //1.100000000000000088817841970012523233890533447265625 

Как было предложено @yshavit в комментариях, когда вы сравниваете двойной поплавок, поплавок преобразуется в двойной (due to numeric promotion), так вы действительно сравнения двух дублей:

System.out.println(new BigDecimal((double) 1.1f)); //1.10000002384185791015625 
System.out.println(new BigDecimal(1.1d)); //1.100000000000000088817841970012523233890533447265625 
+1

Я думаю, что для бонусных очков 'System.out.println (новый BigDecimal ((double) 1.1f))' - чтобы показать, как округленный float расширяется до double. – yshavit

1

число 1,1 не может быть точно представлены в двоичном представлении с плавающей точкой, таким образом, немного отличаются в 4 байта поплавком и 8 байт двойное представление.

Поэтому два последних являются ложными, потому что они сравнивают double с поплавком для числа, которое не может быть точно представлено.
Другой просто сравнивает двойной с двойным или использовать значение, которое точно можно представить как 1,0

+0

затем объясните «assert 1d == 1f; // true» – logger

+0

прочитал мой обновленный ответ 1.1 cannor быть представленным – AlexWien

2

Запуск этой программы

public static void main(String... args) { 
    System.out.println("1.1f bits: " + Long.toBinaryString(Double.doubleToLongBits(1.1f))); 
    System.out.println("1.1d bits: " + Long.toBinaryString(Double.doubleToLongBits(1.1d))); 
} 

это yelds

1.1f as double: 11111111110001100110011001100110100000000000000000000000000000 
1.1d as double: 11111111110001100110011001100110011001100110011001100110011010 

причина этого поведения заключается в том, что буква 1.1, то есть десятичная дробь 1.1 dec не имеет конечного представления в виде двоичной фракции и хранения IEEE-754 es как двоичные дроби. Таким образом, он округлен, и результат зависит от количества бит, доступных во время преобразования (кстати, преобразование даже не выполняется во время выполнения - это работа компилятора).

Когда сравниваются операторы разного размера, тем меньше поощряется. Обратите внимание, что это не так, что в два раза и с плавающей точкой не может быть равен:

3.1f != 3.1d 
3.0f == 3.0d 
Смежные вопросы