【发布时间】:2020-06-19 06:20:48
【问题描述】:
cppreference 文档指出,std::round 在“中途情况”下会专门从零舍入。虽然文字 0.5 是这样,但 std::sin(pi/6) 却不是这样。我认为这可能是一个浮点错误,所以我打印了这个值,但它正是0.5。然而,在检查二进制表示之后,我可以看到它们确实以不同的方式表示。我在下面提供了用于进行这些检查的代码。
#include <iostream>
#include <stdio.h>
#include <cmath>
int main(int argc, char * argv[])
{
double const pi = std::acos(-1);
double const a = std::sin(pi/6);
double const b = 0.5;
std::cout << "round(" << a << ") = " << std::round(a) << "\n";
auto pa = reinterpret_cast<const unsigned char *>(&a);
auto pb = reinterpret_cast<const unsigned char *>(&b);
std::cout << "a = 0x";
for (size_t i = 0; i != sizeof(double); ++i) {
printf("%02x", pa[i]);
}
std::cout << "\nb = 0x";
for (size_t i = 0; i != sizeof(double); ++i) {
printf("%02x", pb[i]);
}
std::cout << "\n";
}
round(0.5) = 0
round(0.5) = 1
a = 0xffffffffffffdf3f
b = 0x000000000000e03f
所以我的问题是这种舍入行为是 c++ 规范的一部分还是这是一个错误?无论如何,是否有一些通用的方法可以“纠正”sin 返回的值的表示?我不确定它是什么格式,因为根据我对 IEEE-754 的了解,它看起来应该是 NaN。虽然据我了解,c++ 不保证 IEEE-754 浮点表示?
【问题讨论】:
-
如果你用
std::setprecision(17)打印更多小数位,你会看到a实际上是0.49999999999999994,输出也会四舍五入。 -
这些不是 NaN,但您可以通过
isnan进行检查。你知道std::hexfloat吗? -
pi实际上是一个无理值,因此pi/6的浮点计算值(具有有限精度)将始终是一个近似值。这意味着计算sin(pi/6)的结果也将是一个近似值,不一定等于0.5。计算值是大于、等于还是小于0.5是特定于实现(编译器和主机系统)的。如果std::round(sin(pi/6))产生的值为零,则对于您的实现,它表明实际值小于0.5。以更高的精度打印该值将证实这一点。
标签: c++ floating-point rounding