Существует ли общий стандартный способ хранения чисел с плавающей точкой в ​​std :: set и / или std :: unordered_set? - PullRequest
1 голос
/ 23 апреля 2019

Мы знаем, что NaN! = NaN для IEEE плавает. В результате ряд «очевидных» операций с плавающими числами в пинтах имеет скрытые ошибки, в которых NaN в данных могут ужасно запутаться. Например:

Очевидный подход примерно такой:

template <typename T>
struct NaNSafeEqual0 {
    bool operator()(const T& a, const T& b) const {
        if (!(a == a) || !(b == b)) {
            return (a == a) == (b == b); // <- Still has a gotcha: see below.
        }
        return a == b; // This deals with 0.0 == -0.0;
    }
};

Это осложняется тем фактом, что std::hash<float> хэширует битовые значения (по крайней мере, в некоторых реализациях), поэтому для std::unordered_set<float, std::hash<float>, ...> нам нужна наша KeyEqual функция для сравнения KeyEqual()(NaN, NaN) == true, но KeyEqual()(NaN, -NaN) == false. Для std::unordered_set мы можем выполнить побитовое сравнение, за исключением того, что нам все еще нужно -0 == 0.

Я почти уверен, что могу написать надежный (может быть, не быстрый?) NaNSafeLess<T> и NaNSafeEqual<T>, но такие вещи, как известно, сложно сделать правильно, и, более того, сложно сделать это универсальным. Я думаю, что это, вероятно, правильно для встроенных типов POD, хотя оно не является полностью универсальным, потому что оно будет static_assert при sizeof(T) > sizeof(std::size_t), и использует union хитрым способом.

template <typename T>
    struct NaNSafeEqual {
    std::size_t operator()(const T& a, const T& b) const {
        if (a == a || b == b) {
            return a == b; // At least one isn't nan.
        }
        static_assert(sizeof(T) <= sizeof(std::size_t));
        union {
            T data;
            std::size_t s;
        } ua, ub;
        ua.s = 0;
        ub.s = 0;
        ua.data = a;
        ub.data = b;
        return ua.s == ub.s;
    }
};

Итак: существует ли общий стандартный способ записи less и equal, который позволяет безопасно хранить значения с плавающей точкой в ​​std :: set и / или std :: unordered_set? Если нет, предоставляет ли Boost или какая-либо другая широко используемая библиотека?

...