Сообщество Engee

Приближенное равенство

Автор
avatar-maximsidorovmaximsidorov
Notebook

Пример использования приближённого сравнения чисел в Julia

Рассматривается алгоритм приближённого равенства () в языке программирования Julia, позволяющий корректно сравнивать числа с плавающей точкой.

Введение

При работе с числами с плавающей точкой в программировании часто возникают трудности при сравнении значений из-за ограниченной точности представления этих чисел в памяти компьютера.

Например, результат вычисления 0.1 + 0.2 может быть не совсем равным 0.3. Чтобы корректно проверять, близки ли два таких числа друг к другу, используют специальные методы приближённого равенства.

В языке Julia за это отвечает функция (или isapprox()), которая позволяет сравнивать значения с учётом возможных ошибок округления. Этот механизм широко применяется в научных вычислениях, тестировании, обработке данных и других сферах, где требуется высокая точность сравнений.

Этот пример демонстрирует, как работает оператор на разных наборах пар чисел.

Применение приближенного равенства

Создадим список пар значений, которые будем сравнивать

testvalues = [
    # Примеры с очень большими числами:
    [100000000000000.01,        100000000000000.011], # различаются последним десятичным знаком
    
    # Числа среднего порядка:
    [100.01,                    100.011],             # отличаются больше, чем допустимая погрешность
    
    # Операции деления, где результат может быть неточным из-за представления float:
    [10000000000000.001 / 10000.0, 
     1000000000.0000001000],                          # должно быть равно 1000000000000.001 / 10000 = 100000000.0000001
    
    # Очень маленькие положительные числа:
    [0.001,                     0.0010000001],        # разница в миллионных долях
    
    # Очень маленькое число и ноль:
    [0.000000000000000000000101, 0.0],                # почти равно нулю?

    # Математически равные выражения:
    [sqrt(2) * sqrt(2),         2.0],                 # sqrt(2)^2 = 2, но что получится в результате округлений?
    
    # С отрицательными числами:
    [-sqrt(2) * sqrt(2),       -2.0],                 # аналогично для отрицательного случая

    # Число π записанное разным количеством знаков:
    [3.14159265358979323846,    3.14159265358979324] # различаются на один знак после запятой
]
8-element Vector{Vector{Float64}}:
 [1.0000000000000002e14, 1.0000000000000002e14]
 [100.01, 100.011]
 [1.0000000000000002e9, 1.0000000000000001e9]
 [0.001, 0.0010000001]
 [1.01e-22, 0.0]
 [2.0000000000000004, 2.0]
 [-2.0000000000000004, -2.0]
 [3.141592653589793, 3.141592653589793]

Сравнение чисел

Каждая пара из списка testvalues последовательно берётся и сохраняется в переменные (x, y).

Далее происходит вывод информации:

  • выводится значение x, дополненное справа пробелами до длины 21;
  • затем выводится символ "≈";
  • значение y, дополненное слева пробелами до длины 22;
  • после двоеточия выводится результат применения оператора : true или false.
  • таким образом мы можем сразу видеть, какие пары считаются приблизительно равными.
# Проходимся циклом по всем парам (x, y) из массива
for (x, y) in testvalues
    # Выводим результат сравнения x ≈ y вместе со значениями
    println(
        rpad(x, 21),                      # дополняем x пробелами справа до ширины 21
        " ≈ ",                            # операция приближенного равенства
        lpad(y, 22),                      # дополняем y пробелами слева до ширины 22
        ": ",                             # разделитель перед логическим результатом
        x  y                             # вызываем функцию ≈(x, y) -> Bool
    )
end
1.0000000000000002e14 ≈  1.0000000000000002e14: true
100.01                ≈                100.011: false
1.0000000000000002e9  ≈   1.0000000000000001e9: true
0.001                 ≈           0.0010000001: false
1.01e-22              ≈                    0.0: false
2.0000000000000004    ≈                    2.0: true
-2.0000000000000004   ≈                   -2.0: true
3.141592653589793     ≈      3.141592653589793: true

Оператор . Как он работает?

Оператор в Julia по умолчанию эквивалентен вызову функции isapprox(x, y), использующей следующую формулу:

Где:

  • (absolute tolerance): минимальная абсолютная погрешность;
  • (relative tolerance): относительная погрешность.

По умолчанию параметры определяются так:

  • atol = 0
  • rtol = sqrt(eps()) ≈ 1.49e−8

Подробную справку по функции можно также получить в документации.

Заключение

В этом примере мы рассмотрели применение механизма приближённого равенства в языке программирования Julia.

Мы использовали оператор для анализа различий между вещественными числами в контексте неточностей машинной арифметики.

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

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

Пример разработан с использованием материалов Rosetta Code