Сравнение float и двойных примитивов в Java

я наткнулся на странный уголок Java.(Мне это кажется странным)

double dd = 3.5;          
float ff = 3.5f;
System.out.println(dd==ff);   

o / p: true

double dd = 3.2;
float ff = 3.2f;
System.out.println(dd==ff);

o / p: false

я заметил, что если мы сравним любые два значения (float и double, как я упоминал в Примере) с .5 или .0 как 3.5, 234.5, 645.0 тогда выход true т. е. два значения равны, иначе выход будет false хотя они равны.

даже я пытался сделать метод strictfp но не повезло. Я что-то упускаю.

5 ответов


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

сжатие бесконечно многих вещественных чисел в конечное число битов требует приблизительного представления....

- - - редактировать, чтобы показать, что означает приведенная выше цитата - - -

вы никогда не должны сравнивать поплавки или двойники для равенства; потому что вы не можете гарантировать, что номер, который вы назначаете поплавок или двойник точны.

Так

 float x = 3.2f;

не приводит к поплавку со значением 3.2. Это приводит к поплавку со значением 3.2 плюс или минус очень небольшая ошибка. Скажем, 3.19999999997 f. Теперь должно быть ясно, почему сравнение не сработает.

чтобы сравнить поплавки для равенства здраво, вам нужно проверить, является ли значение "достаточно близко"к тому же значению, например

float error = 0.000001 * second;
if ((first >= second - error) || (first <= second + error)) {
   // close enough that we'll consider the two equal
   ...
}

разница в том, что 3.5 может быть представлена ровно как float и double - а 3.2 не может быть представлен точно в любого вида... и два ближайших приближения различны.

представьте, что у нас было два фиксированных точности decimal типы, один из которых хранил 4 значащих цифры и один из которых хранил 8 значащих цифр, и мы попросили каждого из них сохранить число, ближайшее к "третьему" (однако мы могли бы это сделать). Затем один будет иметь значение 0.3333 и один будет иметь значение 0.33333333.

сравнение равенства между float и double сначала преобразует float до double а затем сравнивает два, что эквивалентно преобразованию 0.3333 в нашем" малом десятичном " типе в 0.3330000. Затем он сравнил бы 0.333330000 и 0.33333333 для равенства и дал бы результат false.


общая реализация чисел с плавающей запятой, IEEE754, позволяет точное представление только тех чисел, которые имеют короткое, конечное двоичное разложение, т. е. которые являются суммой конечно многих (близлежащих) степеней двух. Все остальные числа не могут быть точно представлены.

С float и double имеют разные размеры, представление в обоих типах для непредставимого значения отличается, и поэтому они сравниваются как неравный.

(том длина двоичной строки является размер мантиссы, так что это 24 для float, 53 в double и 64 для 80-битного поплавка расширенной точности (не в Java). The масштаб определяется экспонентой.)


плавающая точка-это двоичный формат, и он может представлять числа как сумму степеней 2. например, 3.5 is 2 + 1 + 1/2 - ...

float 3.2 f как приближение 3.2 is

2 + 1 + 1/8+ 1/16+ 1/128+ 1/256+ 1/2048+ 1/4096+ 1/32768+ 1/65536+ 1/524288+ 1/1048576+ 1/4194304 + a small error

однако двойной 3.2 d в качестве приближения 3.2 является

2 + 1 + 1/8+ 1/16+ 1/128+ 1/256+ 1/2048+ 1/4096+ 1/32768+ 1/65536+ 1/524288+ 1/1048576+ 1/8388608+ 1/16777216+ 1/134217728+ 1/268435456+ 1/2147483648+ 1/4294967296+ 1/34359738368+ 1/68719476736+ 1/549755813888+ 1/1099511627776+ 1/8796093022208+ 1/17592186044416+ 1/140737488355328+ 1/281474976710656+ 1/1125899906842624 + a smaller error

когда вы используете плавающую точку, вам нужно использовать соответствующее округление. Если вы используете BigDecimal вместо этого (и многие люди это делают), он имеет округление.

double dd = 3.2;          
float ff = 3.2f;
// compare the difference with the accuracy of float.
System.out.println(Math.abs(dd - ff) < 1e-7 * Math.abs(ff));

BTW код, который я использовал для печати дроби для двойника.

double f = 3.2d;
double f2 = f - 3;
System.out.print("2+ 1");
for (long i = 2; i < 1L << 54; i <<= 1) {
  f2 *= 2;
  if (f2 >= 1) {
    System.out.print("+ 1/" + i);
    f2 -= 1;
  }
}
System.out.println();

Это должно работать:

BigDecimal ddBD = new BigDecimal(""+dd);
BigDecimal ffBD = new BigDecimal(""+ff);

// test for equality
ddBO.equals(ffBD);

С BigDecimal когда вы хотите сравнить поплавки или удваивается и всегда используйте конструктор BigDecimal с Строковый параметр!