浮点数舍入误差
Float rounding off error
#include<iostream>
long myround(float f)
{
if (f >= UINT_MAX) return f;
return f + 0.5f;
}
int main()
{
f = 8388609.0f;
std:cout.precision(16);
std::cout << myround(f) << std::endl;
}
输出:8388610.0
我正在尝试理解输出。下一个大于的浮点数
8388609.0 是 8388610 但为什么四舍五入后的值不是 8388609?
如果您将示例更改为使用 double
,那么错误就会消失。问题是 float
比 double
可以存储的有效数字位数更有限。将 0.5 添加到您的值只会超出浮点数的精度限制,导致它执行一些舍入。在这种情况下,8388609.0f + 0.5f == 8388610.0f
.
#include<iostream>
long myround(double f)
{
if (f >= UINT_MAX) return f;
return f + 0.5;
}
int main()
{
double f = 8388609.0;
std::cout.precision(16);
std::cout << myround(f) << std::endl;
}
如果您继续为您的号码添加数字,它最终也会失败 double
。
编辑:
您可以使用 static_assert
轻松测试它。这在我的平台 static_assert(8388609.0f + 0.5f == 8388610.0f, "");
上编译。它可能会在您的编译器上编译。
IEEE-754 定义了几种可能的舍入模式,但在实践中,几乎经常使用的是 "round to nearest, ties to even"。这也被称为"Banker's rounding",无缘无故任何人都可以辨别。
"Ties to even" 表示如果浮点计算的 to-be-rounded 结果恰好位于两个可表示数字的中间,则舍入将沿使结果的 LSB 为零的方向进行。在您的情况下,8388609.5 介于 8388609 和 8388610 之间,但只有后者的最后一位为零,因此四舍五入是向上的。如果您改为传入 8388610.0,则结果将向下舍入;如果你通过了8388611.0,它会向上舍入。
#include<iostream>
long myround(float f)
{
if (f >= UINT_MAX) return f;
return f + 0.5f;
}
int main()
{
f = 8388609.0f;
std:cout.precision(16);
std::cout << myround(f) << std::endl;
}
输出:8388610.0
我正在尝试理解输出。下一个大于的浮点数 8388609.0 是 8388610 但为什么四舍五入后的值不是 8388609?
如果您将示例更改为使用 double
,那么错误就会消失。问题是 float
比 double
可以存储的有效数字位数更有限。将 0.5 添加到您的值只会超出浮点数的精度限制,导致它执行一些舍入。在这种情况下,8388609.0f + 0.5f == 8388610.0f
.
#include<iostream>
long myround(double f)
{
if (f >= UINT_MAX) return f;
return f + 0.5;
}
int main()
{
double f = 8388609.0;
std::cout.precision(16);
std::cout << myround(f) << std::endl;
}
如果您继续为您的号码添加数字,它最终也会失败 double
。
编辑:
您可以使用 static_assert
轻松测试它。这在我的平台 static_assert(8388609.0f + 0.5f == 8388610.0f, "");
上编译。它可能会在您的编译器上编译。
IEEE-754 定义了几种可能的舍入模式,但在实践中,几乎经常使用的是 "round to nearest, ties to even"。这也被称为"Banker's rounding",无缘无故任何人都可以辨别。
"Ties to even" 表示如果浮点计算的 to-be-rounded 结果恰好位于两个可表示数字的中间,则舍入将沿使结果的 LSB 为零的方向进行。在您的情况下,8388609.5 介于 8388609 和 8388610 之间,但只有后者的最后一位为零,因此四舍五入是向上的。如果您改为传入 8388610.0,则结果将向下舍入;如果你通过了8388611.0,它会向上舍入。