【问题标题】:Why does std::round(sin(pi/6)) not equal 1?为什么 std::round(sin(pi/6)) 不等于 1?
【发布时间】: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


【解决方案1】:

问题是您没有打印具有足够有效数字的值。当我使用std::setprecision(20) 提高精度时,我得到:round(0.49999999999999994449) = 0

您可以通过更改代码或在此在线计算器底部的十六进制字段中输入3fdfffffffffffff 来亲自查看:https://baseconvert.com/ieee-754-floating-point

【讨论】:

    【解决方案2】:

    该表示看起来像 NaN,因为您正在向后阅读它。 x86/x64 有 little-endian 浮点数。所以你应该从高地址到低地址读取它,产生0x3fdfffff...,当然略小于0.5。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-08
      • 1970-01-01
      • 2013-08-17
      • 1970-01-01
      • 2013-01-05
      • 1970-01-01
      • 2022-04-12
      • 1970-01-01
      相关资源
      最近更新 更多