Я знаю обычные проблемы с арифметикой с плавающей запятой и точностью потери, поэтому это не обычный вопрос о том, почему 0.1 + 0.2 != 0.3
и т.п.
Вместо этого я действительно хотел бы реализовать двоичный предикат в С++ (со 100% стандартным способом), который фактически реализует реальный математический эквивалентность (т.е. рефлексивный, транзитивный и симметричный), так что два двойника находятся в одном классе эквивалентности, если они представляют собой то же самое значение во всех отношениях, выделяя такие угловые случаи, как 0.0
и -0.0
, но рассматривая все значения NaN
как находящиеся в одном классе эквивалентности. (В частности, значение по умолчанию ==
не то, что я хочу, потому что оно не рефлексивно в случае NaN
и не различает 0.0
и negative -0.0
, что я хотел бы быть в разных классы эквивалентности, поскольку они фактически являются разными значениями и приводят к различным поведениям во время выполнения).
Какой самый короткий и простой способ сделать это, который не зависит от типа punning или каким-либо образом определенного поведением? Пока у меня есть:
#include <cmath>
bool equiv(double x, double y)
{
return (x == y && (x != 0.0 || std::signbit(x) == std::signbit(y))) ||
(std::isnan(x) && std::isnan(y));
}
Я считаю, что это обрабатывает угловые случаи, о которых я знаю и описывал ранее, но есть ли какие-либо другие угловые случаи, когда это не обрабатывает то, что мне не хватает? И является ли указанный выше двоичный предикат гарантированным для определения отношения эквивалентности в соответствии со стандартом С++ или является ли какое-либо поведение неопределенным, определяемым реализацией и т.д.?