Сегодня я просматривал некоторый код C ++ (написанный кем-то другим) и нашел этот раздел:
double someValue = ...
if (someValue < std::numeric_limits<double>::epsilon() &&
someValue > -std::numeric_limits<double>::epsilon()) {
someValue = 0.0;
}
Я пытаюсь понять, имеет ли это смысл.
Документация для epsilon()
говорит:
Функция возвращает разницу между 1 и наименьшим значением больше 1, которое может быть представлено [двойным].
Относится ли это и к 0, т. epsilon()
Е. Наименьшее значение больше 0? Или есть числа между 0
и 0 + epsilon
которые могут быть представлены double
?
Если нет, то сравнение не эквивалентно someValue == 0.0
?
numeric_limits<>::epsilon
вводит в заблуждение и не имеет значения. Нам нужно принять 0, если фактическое значение отличается не более чем на 0 от 0. И ε следует выбирать на основе спецификации задачи, а не на машинно-зависимом значении. Я подозреваю, что текущий эпсилон бесполезен, поскольку даже несколько операций FP могут накапливать ошибку больше, чем эта.Ответы:
Предполагая, что 64-битный IEEE double, есть 52-битная мантисса и 11-битная экспонента. Давайте разбить его на куски:
Наименьшее представимое число больше 1:
Следовательно:
Есть ли числа от 0 до эпсилон? Много ... Например, минимальное положительное представимое (нормальное) число:
На самом деле есть
(1022 - 52 + 1)×2^52 = 4372995238176751616
числа от 0 до эпсилон, что составляет 47% всех положительных представимых чисел ...источник
0 <= e < 2048
тогда мантисса умножается на 2 до степениe - 1023
. Например, показатель степени2^0
кодируется какe=1023
,2^1
какe=1024
и2^-1022
какe=1
. Значениеe=0
зарезервировано для субнормалей и реального нуля.2^-1022
является наименьшим нормальным числом. Наименьшее число на самом деле0.0000 00000000 00000000 00000000 00000000 00000000 00000001 × 2^-1022 = 2^-1074
. Это субнормально, это означает, что часть мантиссы меньше 1, поэтому она кодируется показателем степениe=0
.Тест, конечно, не то же самое, что
someValue == 0
. Вся идея чисел с плавающей точкой заключается в том, что они хранят показатель степени и значение. Поэтому они представляют значение с определенным количеством двоичных значащих цифр точности (53 в случае двойного IEEE). Представляемые значения гораздо плотнее упакованы около 0, чем около 1.Чтобы использовать более знакомую десятичную систему, предположим, что вы храните десятичное значение «до 4 значащих цифр» с показателем степени. Тогда следующее представимое значение больше, чем
1
есть1.001 * 10^0
, иepsilon
есть1.000 * 10^-3
. Но1.000 * 10^-4
также представимо, если предположить, что показатель степени может хранить -4. Вы можете поверить мне на слово, что двойник IEEE может хранить показатели меньше, чем показательepsilon
.По одному только этому коду вы не можете понять, имеет ли смысл использовать его
epsilon
в качестве границы, или нет , вам нужно посмотреть на контекст. Может быть,epsilon
это разумная оценка ошибки в произведенном расчетеsomeValue
, а может быть, что это не так.источник
someValue == 0.0
или нет.Существуют числа, которые существуют между 0 и эпсилоном, потому что эпсилон - это разница между 1 и следующим наибольшим числом, которое может быть представлено выше 1, а не разница между 0 и следующим наибольшим числом, которое может быть представлено выше 0 (если это так, то код будет очень мало): -
Используя отладчик, остановите программу в конце main и посмотрите на результаты, и вы увидите, что epsilon / 2 отличается от epsilon, zero и one.
Таким образом, эта функция принимает значения в пределах +/- epsilon и обнуляет их.
источник
Аппроксимация эпсилона (наименьшая возможная разница) вокруг числа (1,0, 0,0, ...) может быть напечатана с помощью следующей программы. Он выводит следующий вывод:
epsilon for 0.0 is 4.940656e-324
epsilon for 1.0 is 2.220446e-16
Немного размышлений проясняет, что чем меньше число, которое мы используем для просмотра значения его эпсилона, тем меньше становится значение эпсилона, потому что показатель степени может адаптироваться к размеру этого числа.
источник
Предположим, мы работаем с игрушечными числами с плавающей запятой, которые помещаются в 16-битный регистр. Есть знаковый бит, 5-битная экспонента и 10-битная мантисса.
Значение этого числа с плавающей запятой - это мантисса, интерпретируемая как двоичное десятичное значение, умноженное на два в степени экспоненты.
Около 1 показатель равен нулю. Таким образом, самая маленькая цифра мантиссы - одна часть в 1024 году.
Около 1/2 показатель степени равен минус один, поэтому наименьшая часть мантиссы вдвое больше. При пятиразрядном показателе он может достигать отрицательного значения 16, и в этот момент наименьшая часть мантиссы стоит одну часть на 32 метра. И при отрицательном показателе 16 значение составляет около одной части в 32k, что намного ближе к нулю, чем эпсилон около того, который мы вычислили выше!
Теперь это игрушечная модель с плавающей запятой, которая не отражает все особенности реальной системы с плавающей запятой, но способность отражать значения, меньшие, чем эпсилон, достаточно схожа с реальными значениями с плавающей запятой.
источник
Разница между
X
и следующим значениемX
варьируется в зависимости отX
.epsilon()
разница только между1
следующим значением1
.Разница между
0
следующим значением0
и нетepsilon()
.Вместо этого вы можете использовать,
std::nextafter
чтобы сравнить двойное значение со0
следующим:источник
Я думаю, что это зависит от точности вашего компьютера. Посмотрите на эту таблицу : вы можете видеть, что если ваш эпсилон представлен двойным, но ваша точность выше, сравнение не эквивалентно
Хороший вопрос в любом случае!
источник
Вы не можете применить это к 0, из-за мантиссы и экспонент. Благодаря показателю степени вы можете хранить очень маленькие числа, которые меньше, чем эпсилон, но когда вы попытаетесь сделать что-то вроде (1.0 - «очень маленькое число»), вы получите 1.0. Эпсилон - это показатель не ценности, а точности значения, который есть в мантиссе. Он показывает, сколько правильных последовательных десятичных цифр числа мы можем сохранить.
источник
С плавающей точкой IEEE между наименьшим ненулевым положительным значением и наименьшим ненулевым отрицательным значением существуют два значения: положительный ноль и отрицательный ноль. Проверка, находится ли значение между наименьшими ненулевыми значениями, эквивалентна проверке на равенство с нулем; однако назначение может оказать влияние, поскольку оно изменит отрицательный ноль на положительный ноль.
Вполне возможно, что формат с плавающей запятой может иметь три значения между наименьшими конечными положительными и отрицательными значениями: положительный бесконечно малый, нулевой без знака и отрицательный бесконечно малый. Я не знаком ни с какими форматами с плавающей запятой, которые на самом деле работают таким образом, но такое поведение было бы совершенно разумным и, возможно, лучше, чем в IEEE (возможно, не настолько лучше, чтобы стоило добавлять дополнительное оборудование для его поддержки, но математически 1) / (1 / INF), 1 / (- 1 / INF) и 1 / (1-1) должны представлять три разных случая, иллюстрирующих три разных нуля). Я не знаю, будет ли какой-либо стандарт C предписывать, чтобы подписанные бесконечно малые числа, если они существуют, должны были бы сравниваться равными нулю. Если это не так, код, подобный приведенному выше, может быть полезен для
источник
Допустим, система не может различить 1.000000000000000000000 и 1.000000000000000000001. то есть 1,0 и 1,0 + 1е-20. Как вы думаете, все еще есть некоторые значения, которые могут быть представлены между -1e-20 и + 1e-20?
источник
epsilon
. Потому что это с плавающей точкой, а не с фиксированной точкой.Также веская причина наличия такой функции является удаление «денормалов» (тех очень маленьких чисел, которые больше не могут использовать подразумеваемое начальное «1» и имеют специальное представление FP). Зачем тебе это делать? Потому что некоторые машины (в частности, некоторые старые Pentium 4) работают очень, очень медленно при обработке ненормальных. Другие просто становятся немного медленнее. Если вашему приложению не нужны эти очень маленькие цифры, сброс их в ноль - хорошее решение. Хорошие места, чтобы рассмотреть это - последние шаги любых фильтров БИХ или функций распада.
См. Также: Почему изменение от 0,1f до 0 снижает производительность в 10 раз?
и http://en.wikipedia.org/wiki/Denormal_number
источник