Как выбрать значение epsilon для плавающей точки? [закрытый]


Поскольку мы знаем, что 0.1 + 0.2 != 0.3 из-за ограниченного представления чисел, нам нужно вместо этого проверить шляпу abs(0.1+0.2 - 0.3) < ε. Вопрос в том, какое значение ε мы обычно выбираем для различных типов? Можно ли оценить его в зависимости от количества битов и количества и типов операций, которые, вероятно, будут выполнены?

3 4

3 ответа:

Вы оцениваете машину Эпсилон, используя алгоритм ниже. Вам нужно умножить этот Эпсилон на целое число 1+(log (number)/log(2)). После того как вы определили это значение для всех чисел в вашем уравнении, вы можете использовать анализ ошибок для оценки значения Эпсилона для конкретного вычисления.

epsilon=1.0

while (1.0 + (epsilon/2.0) > 1.0) {
  epsilon = epsilon /2.0     
}
//Calculate error using error analysis for a + b
epsilon_equation=Math.sqrt(2*epsilon*epsilon)

document.write('Epsilon: ' + epsilon_equation+'<br>')
document.write('Floating point error: ' + Math.abs(0.2 + 0.4 -0.6)+'<br>')
document.write('Comparison using epsilon: ')
document.write(Math.abs(0.2 + 0.4 -0.6)<epsilon_equation)

Следуя вашему замечанию, я попробовал тот же подход в C# , и он, кажется, работает:

using System;

namespace ConsoleApplication
{

    public class Program
    {
        public static void Main(string[] args)
        {
            double epsilon = 1.0;

            while (1.0 + (epsilon/2.0) > 1.0)
            {
                epsilon = epsilon/2.0;
            }

            double epsilon_equation = Math.Sqrt(2*epsilon*epsilon);

            Console.WriteLine(Math.Abs(1.0 + 2.0 - 3.0) < Math.Sqrt(3.0 * epsilon_equation * epsilon_equation));
        }
    }
}

Базовое значение для Эпсилона-это разница между 1.0 и следующим по величине репрезентативным значением. В C++ это значение доступно как std::numeric_limits<T>::epsilon().

Обратите внимание, что, как минимум, вам нужно масштабировать это значение в пропорции к фактическому числу, которое вы тестируете. Кроме того, поскольку точность масштабируется только приблизительно с числовым значением, вы можете увеличить свою маржу на небольшой коэффициент, чтобы предотвратить ложные ошибки:
double epsilon = std::numeric_limits<double>::epsilon();

// C++ literals and math functions are double by default
bool is_near = abs(0.1+0.2 - 0.3) <= 0.3 * (2*epsilon);

Как более полный пример, функция для сравнения дубли:

bool is_approximately_equal(double a, double b) {
  double scale = max(abs(a), abs(b));
  return abs(a - b) <= scale * (2*epsilon);
}
На практике фактическое значение Эпсилона, которое вы должны использовать, зависит от того, что вы делаете и какой тип допуска вам действительно нужен. Числовые алгоритмы обычно имеют допуски точности (среднее и максимальное), а также оценки времени и пространства. Но оценка точности обычно начинается с чего-то вроде characteristic_value * epsilon.

Мне известен следующий подход к точному вычислению предикатов с плавающей запятой: вычислите значение, используя стандартные типы с плавающей запятой, и вычислите ошибку. Обычно предикат может быть задан как p(x) == 0 или p(x) < 0 и т. д. Если абсолютное значение p(x) больше ошибки, то вычисления считаются точными. В противном случае используется интервальная или точная рациональная арифметика.

Можно оценить ошибку по используемому выражению. Я слышал об автомате генераторы этого, но не смогли найти никаких ссылок.

Насколько мне известно, точные вычисления в основном используются для геометрии, и поиск в Google для "точных геометрических вычислений" дает много информации по этой теме.

Вот статья , которая каким-то образом объясняет оценку ошибок.