请告诉我以下C函数之间的区别。
static int mandel(float c_re, float c_im, int count) {
float z_re = c_re, z_im = c_im;
int i;
for (i = 0; i < count; ++i) {
if (z_re * z_re + z_im * z_im > 4.f)
break;
float new_re = z_re*z_re - z_im*z_im;
float new_im = 2.f * z_re * z_im;
z_re = c_re + new_re;
z_im = c_im + new_im;
}
return i;
}
以及下面的
static int mandel(float c_re, float c_im, int count) {
float z_re = c_re, z_im = c_im;
int i;
for (i = 0; i < count; ++i) {
if (z_re * z_re + z_im * z_im > 4.f)
break;
float new_im = 2.f * z_re * z_im;
z_re = c_re + z_re*z_re - z_im*z_im;//I have combined the statements here and removed float new_re
z_im = c_im + new_im;
}
return i;
}
请参阅我对代码更改的评论。该函数为某些输入提供不同的值。浮动是否由于合并了这两种陈述而出错?
在数学中,这两个语句是等价的。然而,在计算机硬件中,它们可能不是
您可能会得到舍入错误,因为初始结果(new_re)被舍入,然后添加到c_re。
正如Niklas所提到的:
中间值以更高精度存储
因此,new_re的结果在存储到new_re时可能会丢失一些浮点,但如果将中间值添加到c_re,则小的c_re值与new_re计算的较低有效值相结合可能有助于最终结果。
在计算数学表达式时,允许C或C++编译器生成的代码以更高的精度保持中间结果。
例如,在x86计算机上,C和C++double
的值通常是64位IEEE754浮点数,但数学处理器堆栈在进行计算时每个值使用80位。
这意味着计算的确切结果将取决于临时存储在内存中的位置以及它在fp堆栈中的保存位置。通常这不是问题,因为临时值的精度高于存储值的精度。。。但这并不总是正确的,因为计算可能是精确地围绕浮点期望的舍入规则设计的。
还要注意的是,编译器提供了特殊的标志,要求严格执行数学评估,或者允许它们非常自由地帮助优化(包括忽略将运算存储到局部变量中,或者将运算重写为理论数学等效版本)。今天的违约通常是有点自由的,而不是非常严格,因为这会损害性能。