浮点舍入误差
On a float rounding error
我不理解以下程序的输出:
int main()
{
float x = 14.567729f;
float sqr = x * x;
float diff1 = sqr - x * x;
double diff2 = double(sqr) - double(x) * double(x);
std::cout << diff1 << std::endl;
std::cout << diff2 << std::endl;
return 0;
}
输出:
6.63225e-006
6.63225e-006
我使用的是VS2010,x86编译器
我希望得到不同的输出
0
6.63225e-006
为什么diff1
不等于0?为了计算sqr - x * x
,编译器将浮点精度提高到两倍。为什么?
浮点寄存器为80位(在大多数现代CPU上)
在表达式中,结果是一个80位的值。只有当它被分配到内存中的某个位置时,它才会被截断为32(浮点)或64(双精度)。如果您将所有内容都保存在寄存器中(尝试使用-O3进行编译),您可能会看到不同的结果。
编译者:-03:
> ./a.out
0
6.63225e-06
float diff1 = sqr - x * x;
double diff2 = double(sqr) - double(x) * double(x);
为什么diff1不等于0?
因为您已经缓存了sqr = x*x
并强制其表示为float
。
为了计算sqr-x*x编译器将浮点精度提高到两倍。为什么?
因为在C标准出现之前,C就是这样做的。我认为现代编译器不受这种约定的约束,但许多编译器仍然遵循这种约定。如果是这样的话,diff1
和diff2
的计算右侧将是相同的。唯一的区别是,在计算了float diff1 = ...
的右侧之后,二重结果被转换回浮点值。
显然,标准允许在这样的表达式中自动将浮点提升为双精度。参见此处
在该页面上查找"自动升级",并查看第一段中的短语。
根据我的理解,如果我们按照这段话,你的sqr=x*x最初也被视为一个二重,但一旦它被存储,它就会被四舍五入为浮点。然后,在diff1=sqr-x*x中,x*x再次被视为二重,sqr也是如此,尽管它已经四舍五入了。因此,它产生的结果与将它们全部铸造为双精度相同:sqr是双精度,但已经四舍五入到浮点精度,x*x也是双精度。
在x86/x64体系结构上,编译器通常会将所有32位浮点运算提升为64位双精度运算;检查输出组件,看看这两种变体是否产生相同的指令。这两种类型之间的唯一区别是存储。
- C++将浮点指针值舍入为小数位数
- Boost::posix_time::ptime舍入到给定的分钟数
- 浮点定向舍入和优化
- 为什么输出精度没有正确舍入?
- 使用设置精度时如何阻止数字向上舍入?
- 如何在使用 MPFR 时在提升多精度中设置舍入模式
- 定义范围,尽管C++舍入误差
- 舍入误差在DFT中给出不正确的tesult
- 如何在 +、-、* 和 / 中舍入误差后计算浮点精度
- 在C++中使用地板函数的舍入误差
- 浮点到双精度转换的舍入误差
- c++ linux x86_64中如何控制双精度计算以避免舍入误差
- 误差C++舍入数字 - 表达式必须具有整数或枚举类型
- R舍入误差与c++舍入误差
- "floor"是否有可能由于浮点舍入误差而返回不准确的结果?
- 射线平面相交:不准确的结果-舍入误差
- 浮点舍入误差
- C++、点和线在2D中的位置对舍入误差和位置具有鲁棒性
- C++中的舍入误差
- std::cbrt的舍入误差