0
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        double firstDouble = scanner.nextDouble();
        double secondDouble = scanner.nextDouble();

        if (Math.abs(secondDouble - firstDouble) < 0.000001) {
            System.out.println("числа равны");
        } else {
            System.out.println("числа не равны");
        }
        System.out.println(firstDouble - secondDouble);
    }
}

Имеем такой код. Просто программа проверки равенства двух вещественных чисел через сравнение их разности с 0.000001. И да, я знаю про класс java.math.BigDecimal(), но в данном случае я использую казуальный метод сравнения примитивов, но попробуйте прогнать 0.000007 и 0.000006. Казалось бы, условие не выполняется, и вывод должен быть - "не равны". Но нет. А главное, я для проверки в самом конце прописал вывод самой разности этих чисел. Там так и вовсе дичь какая-то. Уважаемые знатоки, что и почему происходит?

Rostik
  • 39
  • 8

1 Answers1

2

Я запустил такой код:

    public static void main(String[] args) {
        double firstDouble = 0.000006;
        double secondDouble = 0.000007;
        System.out.println(Math.abs(secondDouble - firstDouble));
    }

Казалось бы: мы должны получить 0.000001 и false. Но мы получаем 9.999999999999997E-7. Если я ничего не напутал, то это можно выразить как 0.000000999999999....

Так происходит из-за механизма хранения чисел с плавающей точкой в компьютерах. Всех тонкостей вам не расскажу, да вы можете найти их сами, скажу лишь то, что суть в следующем: в компьютерах все числа выражены в двоичной системе счисления, а не в десятичной, к какой мы привыкли, из-за чего получившееся число 0.000001 не круглое. Короче говоря, число 0.000001 не может быть полностью выражено в двоичной системе счисления, для у этого у double просто нет места, поэтому происходят такие вот ошибки.

Ссылка

Zhenyria
  • 2,141
  • 2
  • 16
  • 47
  • Если бы это выражалось в т. ч. в том виде, который вы указали, то сравнение этого числа с одной миллионной работало бы корректно, однако этого не происходит. Программа выдаёт, что числа равны, хотя согласно условию сравнения - нет. Так что... – Rostik Jan 08 '21 at 20:13
  • @Rost а что вам не нравится? Ну вот мы получили 9.999999999999997E-7. Это число меньше чем 0.000001, значит true. – Zhenyria Jan 08 '21 at 20:14
  • @Rost что вы хотите мне сказать, я не понимаю. Мы получили true. Да, я знаю, что на самом деле не true. но почему так я вам объяснил. Объясните конкретно, что вы имеете в виду – Zhenyria Jan 08 '21 at 20:18
  • @Rost double и float в принципе не предназначены для точных вычислений. Используйте BigDecimal. Я также дополнил свой ответ ссылкой. – Zhenyria Jan 08 '21 at 20:20
  • Вернее так. Допустим это число real меньше 0.000001. Но так ведь не должно быть при элементарной разности. Допустим, у Java траблы с вещественными числами, но тогда мне непонятно, почему Java Rush на серьёзных щах советует этом метод для сравнения чисел, когда погрешность не сильно важна. Но тут она блин уже равна самому числу с которым сравниваем! Lol – Rostik Jan 08 '21 at 20:20
  • @Rost да, всё верно. Кстати на JavaRush уточняется момент, о котором я вам говорю. Но если вы хотите запускать ракету в космос или просчитать траекторию метеорита, то вам, наверное, не стоит использовать примитивы. Чтобы сравнить граммовку сахара, примитивов достаточно. Сделайте так: уберите один разряд из 0.000001. Проверяйте, что разница меньше 0.00001. Думаю, это допустимая для вас погрешность. Или используйте BigDecimal – Zhenyria Jan 08 '21 at 20:23
  • Да не, ну это полнейший бред, если погрешность метода равна пороговому числу для сравнения. Таким бы я даже сахар мерить не стал. И главное в комментах к той лекции ноль претензий на эту тему... :) Вам спасибо. – Rostik Jan 08 '21 at 20:29