Как сравнивать double
Здравствуйте, gandjustas, Вы писали:
G>Нет. Вообще прямое сравнение чисел с плавающией запятой, вида (a==b) может дать неправильный результат
UN>Не 0.0000001, а EPS — это значение определено в CRT =)
Так. Тут начался беспредел с ужасами про сравнение плавающей точки на равенство. Требуется ликбез. Сравнивать плавающие числа (для педантов — значения переменных типа float или double) вполне можно и даже нужно. Но надо понимать сущность этой плавающей точки. А сущность заключается в том, что числа с фиксированной точкой (целые — это частный случай чисел с фиксированной точкой) имеют абсолютное значение погрешности, в отличие от чисел с плавающей точкой, где значение погрешности находится в прямой пропорциональности от модуля числа. Во всяком случае:
double a=3.1415; double b=a; if(a == b)
Вот если этот if не сработает, то это означает, что компьютер сломался, а процессор издох.
Другое дело, когда числа вычислены разными способами — одно через синус, а другое — через экспоненту. Здесь действительно проверка на равенство скорее всего не сработает. Так же, как и не сработает сравнение с константой. Но это же относится и к целым числам, если скажем, мы нормализуем значения от 0. 1 к 0. 1000000000. То есть, не имеет значения, плавающие это числа или целые. В определенных ситуациях сравнивать их на строгое равенство нельзя. В этих ситуациях надо использовать некую Epsilon. И вот здесь-то и вылезает наружу вся безграмотность. Что такое DBL_EPSILON? — а это вот что. Это минимальное значение, которое при прибавлении его к единице, меняет значение этой единицы. Понимаете? — к единице! Строгой единице, числу 1.0 и ни к какому другому. Поэтому сравнивать числа с плавающей точкой на +/- DBL_EPSILON совершенно бессмысленно. Это сравнение выдает всю глубину невежества и неспособности думать мозгом. Факты таковы — плавающие числа больше 2.0 эту DBL_EPSILON просто не ощущают. Им что прибавляй ее, что что нет — ничего не меняет. Для этих чисел DBL_EPSILON является строгим нулем и просто не существует. В то же время, DBL_EPSILON имеет значение порядка 1e-16. Что это значит? А это значит, что числа в диапазоне Планковских масштабов (типа 1e-34) с точки зрения этой DBL_EPSILON будут все равны. То есть, эта 1e-16 становится слоном в посудной лавке. А ведь постоянная Планка ничуть не хуже скорости света — для этого собственно и были придуманы числа с плавающей точкой, чтобы отображать большие диапазоны значений с неким фиксированным количеством знаков.
Так для чего же все-таки нужна эта самая DBL_EPSILON (ну или FLT_EPSILON)? Нужна-ли? — нужна! Есть ситуации, когда действительно надо сравнивать числа в неком допустимом интервале. В каком? — А вот это как раз и зависит от абсолютного значения чисел и сущности вычислений. Короче говоря, надо эту Epsilon умножить на значение числа. А поскольку у нас два числа, то все усложняется — какое из них брать. То есть, корректное сравнение выглядит так:
if (fabs(a-b) относительной точностью DBL_EPSILON >
Дорого? Да, дорого, а все остальное неправильно, такие дела. Но и это тоже неправильно! Дело в том, что этот DBL_EPSILON определяет разницу в 1 (один!) значащий бит экспоненты в приложении к числу 1.0. На практике такой разницы не встречается — числа либо строго равны, либо могут различаться больше чем на один значащий бит. Поэтому надо брать что-то типа 16*DBL_EPSILON, чтобы игнрорировать разницу в 4 младших бита (или примерно полторы последние значащие десятичные цифры из примерно 16 имеющихся).
Конечно же, есть случаи, когда диапазон чисел более-менее известен и предсказуем. Скажем, 0. 1000. В этом случае, для сравнения на приблизительное равенство можно взять константу, типа 1000*16*DBL_EPSILON. Но надо иметь в виду, что такое сравнение фактически превращает всю идею плавающей точки в фиксированную точку (догадайтесь, почему).
Я вообще поражен уровню невежества — даже в весьма грамотной библиотеке GPC by Alan Murta используется тупое сравнение с константной Epsilon. На диапазонах экранных координат это все равно, что сравнение на строгое равенство, а на 1e-20 алгоритм вообще перестает работать.
И пожалуйста, имейте смелость высмеять от меня в лицо того университетского преподавателя, который скажет вам, что сравнивать плавающие числа на равенство нельзя. Такое высказывание можно простить в школе, на первом уроке информатики, но никак не в универе.
Вроде бы все сказал. Дополняйте.
Безопасно ли сравнение == для типа double?
Что такое «безопасность» в вашем понимании? Да, безопасно сравнивать — компьютер не взорвется, и с вашей карты никто денег не снимет.
17 фев 2017 в 11:13
5 ответов 5
Сортировка: Сброс на вариант по умолчанию
-
Нельзя сравнивать два числа с плавующей запятой между собой, из за того, что числа с плавующей запятой не могут быть представлены точно, по-этому мы не можем полагаться на оператор сравнения. Популярная практика сравнения такая:
#include #include bool is_equal(double x, double y) < return std::fabs(x - y) < std::numeric_limits::epsilon(); >
Отслеживать
user177221
ответ дан 8 июл 2015 в 15:26
Denis Zaikin Denis Zaikin
441 4 4 серебряных знака 6 6 бронзовых знаков
Сравнение на строгое равенство почти никогда не безопасно. Вместо него следует использовать fabs(x-y) < EPS , где EPS - это некая константа, например, 1e-7 .
Единственный случай, когда можно позволить себе строгое сравнение — это если есть некая константа, которую ты мог сам присвоить (явно, без вычислений), и с которой сравниваешь для того, чтобы узнать, было ли переприсваивание. И даже в этом месте надо быть осторожным, потому что известны случаи, когда благодаря оптимизации у сравниваемых величин оказывается разный тип, что приводит к разной точности и неравенству.
Ещё все вычисления с целыми числами до 2 53 точные. Это используется языками с единственным числовым типом (например js). Однако, если ты уверен, что значение целое, то зачем вообще использовать double ? Лучше взять 64-битный целый тип.
Научный форум dxdy
В файле float.h находится константа DBL_EPSILON, которая равна минимальному x такому, что 1.0 + x > x. И далее везде в интернетах утверждается, что для сравнения на равенство чисел a и b достаточно написать
Используется синтаксис C++
if ( fabs ( a — b ) <= DBL_EPSILON * fmax ( fabs ( a ) , fabs ( b ) ) ) {
// a == b с относительной точностью DBL_EPSILON
}
=>
Почему это так? Откуда такое неравенство?
Re: сравнение double в c++
12.12.2011, 14:20
На самом деле вопрос сравнения чисел с плавающей точкой, увы, не прост.
Ошибки округления могут накапливаться, и нужно их как-то оценивать.
Если в Вашем примере a и b имеют тип float или double (и отключена оптимизация, позволяющая хранить промежуточные результаты в регистрах), то проще будет написать (a == b) — результат будет тот же (хотя насчёт double я не совсем уверен, но практический смысл расписывать «длинный» способ отсутствует).
Вообще, «длинный» способ, конечно, корректнее, но тут нужно:
1) подобрать правильное EPSILON;
2) отдельно рассмотреть случай, когда числа a и b близки к нулю.
Оба пункта нужно рассматривать исходя из практических соображений, которые в каждом случае разные.
Например, я в одной из расчётных программ использовал примерно такую функцию:
Используется синтаксис C
bool areEqual ( double x , double y )
{
static double SMALL_NUM = 1.0E-5 ;
if ( abs ( x ) < SMALL_NUM && abs ( y ) < SMALL_NUM )
return abs ( x — y ) < SMALL_NUM ;
else
return abs ( x — y ) < abs ( x ) * SMALL_NUM ;
}
и абсолютная погрешность
при известной величине
связаны между собой как
Так что, когда вам надо найти, что числа равны с заданной погрешностью, то вы оцениваете
В принципе, выбор наибольшей — стандартная процедура — в данном случае перестраховка, поскольку если две величины будут различаться по порядку величины, то и между собой они равны не будут. С другой стороны, она обеспечивает хотя бы коммутативность отношения:
хотя уже ни о какой транзитивности речь не идёт.
И совет на будущее: никогда не полагайтесь на сравнения с погрешностью DBL_EPSILON . Если у вас есть два числа, которые надо сравнить, то они откуда-то происходят, из каких-то измерений или расчётов, и их погрешность всегда выше, чем DBL_EPSILON . Правильно в таком случае использовать погрешность вычислений или измерений, которую вы оцениваете или сами, или требуете от того, кто предоставляет вам сами числа.
Re: сравнение double в c++
12.12.2011, 18:05
Последний раз редактировалось hurtsy 12.12.2011, 18:21, всего редактировалось 1 раз.
max(Im) в сообщении #514633 писал(а):
которая равна минимальному x такому, что 1.0 + x > x
точнее должно быть x>0, а далее по вашему тексту. Константы такие машинно-зависимые. В CBuilder6 их 3 для разных типов чисел с плавающей запятой.
#define DBL_EPSILON 2.2204460492503131E-16
#define FLT_EPSILON 1.19209290E-07F
#define LDBL_EPSILON 1.084202172485504434e-019L
max(Im) в сообщении #514633 писал(а):
Почему это так?
Это связано со стремлением достичь минимальной по абсолютному значению относительной погрешности для разных типов чисел с плавающей запятой и разных по величине чисел. С уважением,
Страница 1 из 1 | [ Сообщений: 4 ] |
Кто сейчас на конференции
Сейчас этот форум просматривают: нет зарегистрированных пользователей
3.4. Сравнение чисел с плавающей точкой с ограниченной точностью
Требуется сравнить значения с плавающей точкой, но при этом выполнить сравнение на равенство, больше чем или меньше чем с ограниченным количеством десятичных знаков. Например, требуется, чтобы 3.33333 и 3.33333333 считались при сравнении с точностью 0.0001 равными.
Напишите свои функции сравнения, которые принимают в качестве параметра ограничение точности сравнения. Пример 3.6 показывает основную методику, используемую в такой функции сравнения.
Пример 3.6. Сравнение чисел с плавающей точкой
#include // для fabs()
using namespace std;
bool doubleEquals(double left, double right, double epsilon)
return (fabs(left — right) < epsilon);
bool doubleLess(double left, double right, double epsilon,
bool orequal = false)
if (fabs(left — right) < epsilon)
// В рамках epsilon, так что считаются равными
bool doubleGreater(double left, double right, double epsilon,
bool orequal = false)
if (fabs(left — right) < epsilon)
// В рамках epsilon, так что считаются равными
return (left > right);
double first = 0.33333333;
double second = 1.0 / 3.0;
// Тест на прямое равенство. Не проходит тогда, когда должно проходить.
// (boolalpha печатает булевы значения как «true» или «false»)
// Новое равенство. Проходит так, как требуется в научном приложении.
// Новое меньше чем
// Новое больше чем
// Новое меньше чем или равно
// Новое больше чем или равно
Далее показан вывод этого примера.
Код примера 3.6 начинается с двух значений — 0.33333333 и того, что компьютер получает в результате деления 1.0 / 3.0. Он с помощью форматирования по умолчанию cout печатает эти два значения. Они кажутся одинаковыми и равными 0.333333. Однако при сравнении этих двух значений они оказываются различными. Значение 1.0 / 3.0 имеет больше значащих цифр, чем 0.33333333, и, следовательно, как полагает машина, эти два числа не равны. Однако в некоторых приложениях может потребоваться, чтобы они считались равными.
Чтобы добиться этого, надо написать собственные функции сравнения чисел с двойной точностью: doubleLess, doubleEquals и doubleGreater, каждая из которых принимает в качестве параметров два значения типа double. Кроме того, doubleLess и doubleGreater имеют дополнительный параметр, который при его равенстве true приводит к тому, что эти функции ведут себя как «меньше или равно» и «больше или равно» соответственно.
Чтобы заставить эти функции учитывать точность, рассмотрим функцию doubleEquals. Вместо того чтобы проверять на равенство, эта функция проверяет, находится ли разность двух чисел в указанном пользователем диапазоне epsilon. (В качестве epsilon пример использует значение 0.0001.) Если это так, то функция возвращает значение true, что означает, что значения одинаковы. Таким образом, равными окажутся значения 0.3333, 0.33333, 0.333333, 0.33333333333 и 0.33333323438.
Чтобы выполнить операцию «меньше чем» и «больше чем», вначале проверьте, не равны ли значения, как это делается в функции doubleEquals. Если так, то при наличии теста на равенство верните true, а в противном случае — false. В противном случае выполните прямое сравнение.
Читайте также
Нельзя просто использовать вычисления с плавающей точкой
Нельзя просто использовать вычисления с плавающей точкой Когда пользовательская программа использует вычисления с плавающей точкой, ядро управляет переходом из режима работы с целыми числами в режим работы с плавающей точкой. Операции, которые ядро должно выполнить
R.2.5.3 Константы с плавающей точкой
R.2.5.3 Константы с плавающей точкой Константы с плавающей точкой состоят из целой части, символа точка, дробной части, e или E, целого показателя с возможным знаком и возможным окончанием, указывающим тип. Целая и дробная части состоят из последовательности десятичных
R.4.3 Значения с плавающей точкой и двойной точностью
R.4.3 Значения с плавающей точкой и двойной точностью Для выражений типа float может использоваться арифметика с обычной точностью. Если значение с плавающей точкой меньшей точности преобразуется в значение типа float равной или большей точности, то изменения значения не
R.4.4 Целочисленные и числа с плавающей точкой
R.4.4 Целочисленные и числа с плавающей точкой Преобразование значения с плавающей точкой к целочисленному типу сводится к «усечению», т.е. отбрасыванию дробной части. Такие преобразования зависят от машины, в частности в какую сторону будет проходить усечение для
5.3. Округление чисел с плавающей точкой
5.3. Округление чисел с плавающей точкой Кирк: Какие, вы говорите, у нас шансы выбраться отсюда? Спок: Трудно сказать точно, капитан. Приблизительно 7824.7 к одному. Стар Трек, «Миссия милосердия» Метод round округляет число с плавающей точкой до целого:pi = 3.14159new_pi = pi.round # 3temp =
5.4. Сравнение чисел с плавающей точкой
5.4. Сравнение чисел с плавающей точкой Печально, но факт: в компьютере числа с плавающей точкой представляются неточно. В идеальном мире следующий код напечатал бы «да», но на всех машинах где мы его запускали, печатается «нет»:x = 1000001.0/0.003y = 0.003*xif y == 1000001.0 puts «да»else puts
Числа с плавающей точкой
Числа с плавающей точкой Числа с плавающей точкой более или менее соответствуют тому, что математики называют «вещественными числами». Они включают в себя числа, расположенные между целыми. Вот некоторые из них: 2.75, 3.16Е7, 7.00 и 2е-8. Очевидно, что любое число с плавающей
Описание переменных с плавающей точкой
Описание переменных с плавающей точкой Переменные с плавающей точкой описываются и инициализируются точно таким же образом, что и переменные целого типа. Ниже приведено несколько примеров: float noah, jonah;double trouble;float planck = 6.63e-
Константы с плавающей точкой
Константы с плавающей точкой Правила языка Си допускают несколько способов записи констант с плавающей точкой. Наиболее общая форма записи константы — это последовательность десятичных цифр со знаком, включающая в себя десятичную точку, затем символ е или Е и
Переполнение и потеря значимости при обработке чисел с плавающей точкой
Переполнение и потеря значимости при обработке чисел с плавающей точкой Что произойдет, если значение переменной типа float выйдет за установленные границы? Например, предположим, что вы умножаете 10е38 на 100 (переполнение) или делите 10е — 37 на 1000 (потеря значимости).
Двоичные числа с плавающей точкой
Двоичные числа с плавающей точкой Числа с плавающей точкой хранятся в памяти в виде двух частей: двоичной дроби и двоичного порядка. Посмотрим, как это делается. Двоичные дроби Обычную дробь .324 можно представить в виде3/10 + 2/100 + 4/1000,где знаменатели — увеличивающиеся
Константы с плавающей точкой
Константы с плавающей точкой Константа с плавающей точкой — это действительное десятичное положительное число. Оно включает целую часть, дробную часть и экспоненту. Константы с плавающей точкой имеют следующий формат
Типы данных с плавающей точкой
Типы данных с плавающей точкой Типы данных с плавающей точкой служат «скользящими окнами» с точностью, подходящей масштабу числа. По своей природе в «плавающих» типах положение десятичной точки не зафиксировано — допустимо хранение в одном и том же столбце одного значения
2.4.2 Константы с Плавающей Точкой
2.4.2 Константы с Плавающей Точкой Константы с плавающей точкой имеют тип double. Как и в предыдущем случае, компилятор должен предупреждать о константах с плавающей точкой, которые слишком велики, чтобы их моно было представить. Вот некоторые константы с плавающей точкой:1.23
2.4.4 Константы с Плавающей Точкой
2.4.4 Константы с Плавающей Точкой Константа с плавающей точкой состоит из целой части, десятичной точки, мантиссы, е или Е и целого показателя стпени (возможно, но не обязательно, со знаком). Целая часть и мантисса обе состоят из последовательности цифр. Целая часть или