使用浮点常量设置和检查
Setting and checking with a float constant
我知道这里有很多关于为什么浮动相等比较通常不是一个好主意的问题。我了解浮点数表示问题、舍入问题、将浮点数无声地提升为双精度数、在位级别依赖算术的危险等。但在我看来,这应该没问题,而且我发现的任何问题似乎都涵盖了这一点:
static const float MARKER = -500.0f; // some value well outside the range of valid values
std::vector<float> some_floats = {MARKER, 0.5f, 100.0f, 9.5f, MARKER, 0.f};
for (size_t i = 0; i< some_floats.size(); ++i) {
if (some_floats[i] == MARKER) {
std::cout << i << std::endl;
} else {
// do some math
}
}
输出符合预期:
0
4
如果我启用了 -Wfloat-equal
(在 gcc 中,但在其他编译器中类似),它会将比较行标记为危险:
comparing floating point with == or != is unsafe
。这里几乎所有的答案都说不要使用 == 或 !=,period。但我不明白为什么这是一个问题。我只设置一次常量,然后在其他任何地方重复使用它,并且永远不会对该常量进行任何操作(例如算术)。 我是不是漏掉了什么? 0.0f
,即使没有设置为常数呢?
只要你有信心MARKER
并且它的副本不会被算术函数之类的改变,做一个简单的比较是没有问题的。
也许考虑不在全局使用 -Wfloat-equal
但在本地禁用警告:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
/* your code */
#pragma GCC diagnostic pop
或便携式等价物:https://www.fluentcpp.com/2019/08/30/how-to-disable-a-warning-in-cpp/
您没有理解何时使用浮点数的原因。您使用浮点数来做数学运算。只要你只比较浮点数就没问题。但是,一旦您对数字进行一些操作并在之后比较它们,它通常不会起作用(即使在某些情况下它可能起作用),因为浮点运算并不精确。这就是人们所说的“不要比较浮点数”的意思。
此外,许多编译器都有用于指定浮点行为的标志。例如,对于 MSVC,请查看 here 。这使得更明显的是,精确比较永远不是一个好主意。
如果你想像这样标记浮动你当然可以这样做,但是,我建议使用 std::numeric_limits<float>::min()
作为标记值是安全的。请确保不要以数学方式操纵这些值。还要考虑到其他开发人员可能会将浮点数的存在视为可以有效操纵这些值的指示符,这并不好。作为替代方案,您可以考虑 std::optional 来标记数字是否有效。
正如对 static
的评论:我进一步建议删除静态,因为确实没有必要,而且性能这个词突然出现在某处。我会将其更改为如下所示:
const float MARKER = std::numeric_limits<float>::min(); // some value well outside the range of valid values
std::vector<float> some_floats = {MARKER, 0.5f, 100.0f, 9.5f, MARKER, 0.f};
for (size_t i = 0; i < some_floats.size(); ++i) {
if (some_floats[i] == MARKER) {
std::cout << i << std::endl;
} else {
// do some math
}
}
我知道这里有很多关于为什么浮动相等比较通常不是一个好主意的问题。我了解浮点数表示问题、舍入问题、将浮点数无声地提升为双精度数、在位级别依赖算术的危险等。但在我看来,这应该没问题,而且我发现的任何问题似乎都涵盖了这一点:
static const float MARKER = -500.0f; // some value well outside the range of valid values
std::vector<float> some_floats = {MARKER, 0.5f, 100.0f, 9.5f, MARKER, 0.f};
for (size_t i = 0; i< some_floats.size(); ++i) {
if (some_floats[i] == MARKER) {
std::cout << i << std::endl;
} else {
// do some math
}
}
输出符合预期:
0
4
如果我启用了 -Wfloat-equal
(在 gcc 中,但在其他编译器中类似),它会将比较行标记为危险:
comparing floating point with == or != is unsafe
。这里几乎所有的答案都说不要使用 == 或 !=,period。但我不明白为什么这是一个问题。我只设置一次常量,然后在其他任何地方重复使用它,并且永远不会对该常量进行任何操作(例如算术)。 我是不是漏掉了什么? 0.0f
,即使没有设置为常数呢?
只要你有信心MARKER
并且它的副本不会被算术函数之类的改变,做一个简单的比较是没有问题的。
也许考虑不在全局使用 -Wfloat-equal
但在本地禁用警告:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
/* your code */
#pragma GCC diagnostic pop
或便携式等价物:https://www.fluentcpp.com/2019/08/30/how-to-disable-a-warning-in-cpp/
您没有理解何时使用浮点数的原因。您使用浮点数来做数学运算。只要你只比较浮点数就没问题。但是,一旦您对数字进行一些操作并在之后比较它们,它通常不会起作用(即使在某些情况下它可能起作用),因为浮点运算并不精确。这就是人们所说的“不要比较浮点数”的意思。
此外,许多编译器都有用于指定浮点行为的标志。例如,对于 MSVC,请查看 here 。这使得更明显的是,精确比较永远不是一个好主意。
如果你想像这样标记浮动你当然可以这样做,但是,我建议使用 std::numeric_limits<float>::min()
作为标记值是安全的。请确保不要以数学方式操纵这些值。还要考虑到其他开发人员可能会将浮点数的存在视为可以有效操纵这些值的指示符,这并不好。作为替代方案,您可以考虑 std::optional 来标记数字是否有效。
正如对 static
的评论:我进一步建议删除静态,因为确实没有必要,而且性能这个词突然出现在某处。我会将其更改为如下所示:
const float MARKER = std::numeric_limits<float>::min(); // some value well outside the range of valid values
std::vector<float> some_floats = {MARKER, 0.5f, 100.0f, 9.5f, MARKER, 0.f};
for (size_t i = 0; i < some_floats.size(); ++i) {
if (some_floats[i] == MARKER) {
std::cout << i << std::endl;
} else {
// do some math
}
}