gpt4 book ai didi

c++ - 避免代码重复定义比较运算符 `<, <=, >, >=, ==, !=` 但考虑到 NaN 的最佳方法?

转载 作者:可可西里 更新时间:2023-11-01 17:59:17 26 4
gpt4 key购买 nike

我是数学,x <= y相当于!(x > y) .这对于浮点运算来说是正确的,在大多数情况下,但并非总是如此。当xy是 NaN,x <= y 等于!(x > y) , 因为比较 NaN任何事情总是返回false .但是,x <= y <=> !(x > y)大多数时候是正确的。

现在,假设我正在编写一个包含浮点值的类,并且我想为这个类定义比较运算符。为了明确起见,假设我正在写一个高精度 float ,它使用一个或多个 double values 在内部存储高精度数字。在数学上,x < y 的定义因为这个类已经定义了所有其他运算符(如果我与比较运算符的通常语义保持一致)。但是NaN让我们打破这种数学上的精确性。所以也许我被迫单独编写许多这些运算符,只是为了考虑 NaN。但是有更好的方法吗?我的问题是:如何尽可能避免代码重复并仍然尊重 NaN 的行为? ?

相关:http://www.boost.org/doc/libs/1_59_0/libs/utility/operators.htm . boost/operators 如何解决这个问题?

注意:我将这个问题标记为 c++因为这就是我的理解。请用该语言编写示例。

最佳答案

我个人会使用与 this answer 中类似的技术它根据 operator<() 定义了比较函数产生严格的弱秩序。对于具有 null 值的类型,这意味着进行比较总是产生 false这些操作将根据 operator<() 定义对所有非空值和 is_null() 提供严格的弱顺序测试。

例如,代码可能如下所示:

namespace nullable_relational {
struct tag {};

template <typename T>
bool non_null(T const& lhs, T const& rhs) {
return !is_null(lhs) && !is_null(rhs);
}

template <typename T>
bool operator== (T const& lhs, T const& rhs) {
return non_null(lhs, rhs) && !(rhs < lhs) && !(lhs < rhs);
}
template <typename T>
bool operator!= (T const& lhs, T const& rhs) {
return non_null(lhs, rhs) || !(lhs == rhs);
}

template <typename T>
bool operator> (T const& lhs, T const& rhs) {
return non_null(lhs, rhs) && rhs < lhs;
}
template <typename T>
bool operator<= (T const& lhs, T const& rhs) {
return non_null(lhs, rhs) && !(rhs < lhs);
}
template <typename T>
bool operator>= (T const& lhs, T const& rhs) {
return non_null(lhs, rhs) && !(lhs < rhs);
}
}

它会像这样使用:

#include <cmath>
class foo
: private nullable_relational::tag {
double value;
public:
foo(double value): value(value) {}
bool is_null() const { return std::isnan(this->value); }
bool operator< (foo const& other) const { return this->value < other.value; }
};
bool is_null(foo const& value) { return value.is_null(); }

同一主题的变体可以是一个比较函数的实现,该比较函数由比较函数参数化,并负责适本地为比较函数提供参数。例如:

namespace compare_relational {
struct tag {};

template <typename T>
bool operator== (T const& lhs, T const& rhs) {
return compare(lhs, rhs, [](auto&& lhs, auto&& rhs){ return lhs == rhs; });
}
template <typename T>
bool operator!= (T const& lhs, T const& rhs) {
return compare(lhs, rhs, [](auto&& lhs, auto&& rhs){ return lhs != rhs; });
}

template <typename T>
bool operator< (T const& lhs, T const& rhs) {
return compare(lhs, rhs, [](auto&& lhs, auto&& rhs){ return lhs < rhs; });
}
template <typename T>
bool operator> (T const& lhs, T const& rhs) {
return compare(lhs, rhs, [](auto&& lhs, auto&& rhs){ return lhs > rhs; });
}
template <typename T>
bool operator<= (T const& lhs, T const& rhs) {
return compare(lhs, rhs, [](auto&& lhs, auto&& rhs){ return lhs <= rhs; });
}
template <typename T>
bool operator>= (T const& lhs, T const& rhs) {
return compare(lhs, rhs, [](auto&& lhs, auto&& rhs){ return lhs >= rhs; });
}
}

class foo
: private compare_relational::tag {
double value;
public:
foo(double value): value(value) {}

template <typename Compare>
friend bool compare(foo const& f0, foo const& f1, Compare&& predicate) {
return predicate(f0.value, f1.value);
}
};

我可以想象有多个这样的操作生成命名空间来支持常见情况的合适选择。另一种选择可能是与 float 不同的顺序,例如,将空值视为最小值或最大值。由于有些人使用 NaN 装箱,因此提供不同 NaN 值的顺序并将 NaN 值排列在合适的位置甚至可能是合理的。例如,使用底层位表示提供浮点值的总顺序,这可能适合将对象用作有序容器中的键,尽管该顺序可能与 operator<() 创建的顺序不同。 .

关于c++ - 避免代码重复定义比较运算符 `<, <=, >, >=, ==, !=` 但考虑到 NaN 的最佳方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32039711/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com