比较运算符的数值精度
Numerical precision of comparison operators
简短的问题:比较运算符的行为是否以最大精度定义?
意思是,如果我有两个精确度相同的数字(x 和 y),当我做 x < y 时,我是否应该总是期待相同的答案?
我意识到这似乎是一个愚蠢的问题,但让我详细说明一下。
我正在使用 double
并且我有一组数字,例如:
0: 62536.5477752959
1: 62536.4840613718
2: 62536.4576412381
3: 62522.8487197062
4: 62536.5473896233
5: 62536.5467941254
6: 62527.3508907998
7: 62536.5477752959
8: 62517.5900098039
9: 62536.5477752959
请注意条目 0、7 和 9 具有相同的值。
当我这样做时(像这样):
int low = 0, high = 0;
for(int i = 0; i < N; ++i) {
if(x[i] < x[low])
low = i;
if(x[i] > x[high])
high = i;
}
cout << "low: " << low << " high: " << high << endl;
我有时得到:low: 8 high: 0
,有时:low: 8 high: 7
我本来期望总是最低的索引值。
有什么想法吗?
[编辑缺少的大括号。]
是的,假设您的浮点类型为 IEEE754。任何两个 double
值 x
和 y
表示正好 x < y
、x == y
或 x > y
中的一个成立,除了一些极端情况,例如 +Inf、-Inf 或 NaN。
当您使用十进制表示法表示浮点值时,开始出现混淆;例如没有 double
62536.5477752959(或您列表中与此相关的任何其他人)。
您提供的数字已被调试器/标准输出器截断,它们不是您提供的实际算法中使用的数字。请放心,相同的十进制数 总是 产生相同的 double
,这里没有任意选择:IEEE754 要求选择最接近的 double
。
如需进一步阅读,请参阅 Is floating point math broken?
最后,将int i
替换为int i = 0
。目前您的程序的行为是 undefined.
您可以使用库来避开 behaviour/limitations 内置类型:
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <algorithm>
#include <iostream>
int main() {
using Float = boost::multiprecision::cpp_dec_float_100;
std::vector<Float> values = {
Float{ "62536.5477752959" }, Float{ "62536.4840613718" }, Float{ "62536.4576412381" }, Float{ "62522.8487197062" },
Float{ "62536.5473896233" }, Float{ "62536.5467941254" }, Float{ "62527.3508907998" }, Float{ "62536.5477752959" },
Float{ "62517.5900098039" }, Float{ "62536.5477752959" },
};
auto hilo = std::minmax_element(values.begin(),values.end());
std::cout << "low: " << *hilo.first << " high: " << *hilo.second << std::endl;
}
版画
low: 62517.6 high: 62536.5
要打印索引:
// indexes:
std::cout << "low: " << (hilo.first-values.begin()) << " high: " << (hilo.second-values.begin()) << std::endl;
简短的问题:比较运算符的行为是否以最大精度定义? 意思是,如果我有两个精确度相同的数字(x 和 y),当我做 x < y 时,我是否应该总是期待相同的答案?
我意识到这似乎是一个愚蠢的问题,但让我详细说明一下。
我正在使用 double
并且我有一组数字,例如:
0: 62536.5477752959
1: 62536.4840613718
2: 62536.4576412381
3: 62522.8487197062
4: 62536.5473896233
5: 62536.5467941254
6: 62527.3508907998
7: 62536.5477752959
8: 62517.5900098039
9: 62536.5477752959
请注意条目 0、7 和 9 具有相同的值。
当我这样做时(像这样):
int low = 0, high = 0;
for(int i = 0; i < N; ++i) {
if(x[i] < x[low])
low = i;
if(x[i] > x[high])
high = i;
}
cout << "low: " << low << " high: " << high << endl;
我有时得到:low: 8 high: 0
,有时:low: 8 high: 7
我本来期望总是最低的索引值。
有什么想法吗?
[编辑缺少的大括号。]
是的,假设您的浮点类型为 IEEE754。任何两个 double
值 x
和 y
表示正好 x < y
、x == y
或 x > y
中的一个成立,除了一些极端情况,例如 +Inf、-Inf 或 NaN。
当您使用十进制表示法表示浮点值时,开始出现混淆;例如没有 double
62536.5477752959(或您列表中与此相关的任何其他人)。
您提供的数字已被调试器/标准输出器截断,它们不是您提供的实际算法中使用的数字。请放心,相同的十进制数 总是 产生相同的 double
,这里没有任意选择:IEEE754 要求选择最接近的 double
。
如需进一步阅读,请参阅 Is floating point math broken?
最后,将int i
替换为int i = 0
。目前您的程序的行为是 undefined.
您可以使用库来避开 behaviour/limitations 内置类型:
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <algorithm>
#include <iostream>
int main() {
using Float = boost::multiprecision::cpp_dec_float_100;
std::vector<Float> values = {
Float{ "62536.5477752959" }, Float{ "62536.4840613718" }, Float{ "62536.4576412381" }, Float{ "62522.8487197062" },
Float{ "62536.5473896233" }, Float{ "62536.5467941254" }, Float{ "62527.3508907998" }, Float{ "62536.5477752959" },
Float{ "62517.5900098039" }, Float{ "62536.5477752959" },
};
auto hilo = std::minmax_element(values.begin(),values.end());
std::cout << "low: " << *hilo.first << " high: " << *hilo.second << std::endl;
}
版画
low: 62517.6 high: 62536.5
要打印索引:
// indexes:
std::cout << "low: " << (hilo.first-values.begin()) << " high: " << (hilo.second-values.begin()) << std::endl;