public class Main {
public static void main(String[] args) {
System.out.println(areEqualByThreeDecimalPlaces(-3.1759D, -3.175D));
System.out.println(areEqualByThreeDecimalPlaces(3.174D, 3.175D));
System.out.println(areEqualByThreeDecimalPlaces(-3.0D, -3.0D));
}
public static boolean areEqualByThreeDecimalPlaces(double num1, double num2) {
float f = (float) Math.abs(num1 - num2)*1000f;
double d = Math.abs(num1 - num2)*1000d;
//double d = 1.000000d;
System.out.println(String.format("double: %f float: %f%nd: %f\t\t f: %f", Math.floor(d), Math.floor(f), d, f));
return Math.floor(f) < 1;
}}
В приведенном выше коде я пытаюсь проверить, равны ли два двойных значения, отправленные в качестве параметров в areEqualByThreeDecimalPlaces(), с точностью до 3 десятичных знаков. В рамках этого метода я попытался понять, почему Math.floor() с двойной переменной d не работает должным образом после присвоения вычисленного значения с помощью Math.abs() * 1000d. Похоже, это сработало для переменной floatf после того, как я использовал приведение типа float на Math.abs() - это меня немного смутило. Почему функция Math.floor() не работает должным образом для переменной doubled, поскольку, похоже, она работает для переменной floatf?
Вывод:
double: 0.000000 float: 0.000000
d: 0.900000 f: 0.900000
true
double: 0.000000 float: 1.000000
d: 1.000000 f: 1.000000
false
double: 0.000000 float: 0.000000
d: 0.000000 f: 0.000000
true
Process finished with exit code 0
Это результат, который я получаю с помощью приведенного выше кода. Здесь секции double: и float: являются выходом Math.floor(d) и Math.floor(f). Неожиданное поведение происходит со вторым набором значений, отправленных main(), значениями, заданными этой командой System.out.println(areEqualByThreeDecimalPlaces(3.174D, 3.175D));. Для переменной doubled вывод должен выглядеть так же, как вывод для переменной floatf, однако Math.floor(d) возвращает 0.000000 там, где я ожидал, это будет 1.000000.
Числа с плавающей запятой и двойные числа могут лишь приблизительно представлять десятичные дроби. В зависимости от эффектов округления разница может быть больше или меньше ожидаемой. Ваш конкретный тест (равный тремя десятичными знаками) плохо определен, нет правильного решения, использующего двоичную арифметику с плавающей запятой.




Замените свой последний оператор печати следующим:
System.out.println(String.format("double: %2.15f float: %f%nd: %2.15f\t\t f: %f", Math.floor(d), Math.floor(f), d, f));
Это печатает числа с большим количеством десятичных знаков. Тогда вы можете видеть, что d действительно меньше, чем 1. Это происходит потому, что числа с плавающей запятой (как double или float) обычно не могут точно представлять десятичные значения.
Это связано с тем, что числа с плавающей запятой внутри представлены как двоичные значения (основание 2). Таким образом, в десятичном представлении 1/5 равно 0,2, но это бесконечное число с плавающей запятой в представлении с основанием 2, например, 1/3 находится в десятичном представлении 0,333 ....
Было бы даже лучше напечатать new BigDecimal(d) вместо d, чтобы получить точное десятичное значение. %2.15f не может быть всей правдой.
Мне несколько неудобно последнее предложение здесь - числа с плавающей запятой являются точные значения, они просто не обязательно являются точным результатом предыдущей операции. Например, если у вас есть типы с плавающей запятой с двумя десятичными цифрами и вы попытаетесь представить 1/3, результатом будет точно 0,33, но это не совсем «одна треть». Это операции, которые являются приблизительными, а не сами значения.
@Roland Illig, да, %2.15f не может быть всей правдой, но здесь этого достаточно, чтобы показать причину проблемы.
%2.15f здесь хватает только по стечению обстоятельств. Это могло также привести к неправильному направлению из-за ошибок округления, что, в конце концов, и является той проблемой, которая приводит к этому вопросу. Поэтому это плохой совет, поскольку он не работает надежно в подобных ситуациях. Чтобы давать полезные ответы и получать положительные отзывы, вам лучше писать ответы, которые работают во всех ситуациях и улучшают знания того, кто задает вопрос.
Пожалуйста, укажите в качестве результата то, что вы ожидали, и что вы получили.