为什么功能层在这种情况下会产生不同的结果?

时间:2016-12-30 10:41:48

标签: c floating-point rounding

在此示例中,floor的行为有所不同,我不明白为什么:

printf("floor(34000000.535 * 100 + 0.5) : %lf \n", floor(34000000.535 * 100 + 0.5));
printf("floor(33000000.535 * 100 + 0.5) : %lf \n", floor(33000000.535 * 100 + 0.5));

此代码的输出为:

floor(34000000.535 * 100 + 0.5) : 3400000053.000000
floor(33000000.535 * 100 + 0.5) : 3300000054.000000

为什么第一个结果不能像我们预期的那样等于3400000054.0?

3 个答案:

答案 0 :(得分:3)

C中的

double并不代表可以在文本中表达的每个可能的数字。

double通常可代表约2 64 个不同的数字。当34000000.535编码为binary floating point时,33000000.535double都不在该集合中。而是使用最接近的可表示数字。

Text             34000000.535
closest double   34000000.534999996423...
Text             33000000.535
closest double   33000000.535000000149...

使用double作为二进制浮点数,乘以非幂2,如100.0,可能会引入额外的舍入差异。然而在这些情况下,它仍会产生产品,一个刚好高于xxx.5,另一个高于xxx.5。

添加0.5,简单的2次幂,不会产生舍入问题,因为与3x00000053.5相比,该值并非极端。

将中间结果看作更高的打印精度可以很好地显示典型的逐步过程。

#include <stdio.h>
#include <float.h>
#include <math.h>

 void fma_test(double a, double b, double c) {
   int n = DBL_DIG + 3;
   printf("a b c      %.*e %.*e %.*e\n", n, a, n, b, n, c);
   printf("a*b        %.*e\n", n, a*b);
   printf("a*b+c      %.*e\n", n, a*b+c);
   printf("a*b+c      %.*e\n", n, floor(a*b+c));
   puts("");
 }

int main(void) {
  fma_test(34000000.535, 100, 0.5);
  fma_test(33000000.535, 100, 0.5);
}

输出

a b c      3.400000053499999642e+07 1.000000000000000000e+02 5.000000000000000000e-01
a*b        3.400000053499999523e+09
a*b+c      3.400000053999999523e+09
a*b+c      3.400000053000000000e+09

a b c      3.300000053500000015e+07 1.000000000000000000e+02 5.000000000000000000e-01
a*b        3.300000053500000000e+09
a*b+c      3.300000054000000000e+09
a*b+c      3.300000054000000000e+09

问题比这个简单的答案更复杂,因为各种平台可以1)使用更高精度的数学,如long double或2)很少,使用十进制浮点double 。所以代码的结果可能会有所不同。

答案 1 :(得分:0)

问题已经回答here

基本浮点数只是近似值。如果我们有这样的程序:

float a = 0.2 + 0.3;
float b = 0.25 + 0.25;

if (a == b) {
    //might happen
}
if (a != b) {
    // also might happen
}

唯一有保障的是a-b相对较小。

答案 2 :(得分:-1)

使用code that shows the representation of floats in memory as sum of terms,我们得到:

 func prepareForReuse() {
     super. prepareForReuse()
     myImage.image = nil
 }

在这两种情况下,此代码将在内存中输出由floor返回的值的表示。

使用main() { float x=floor(34000000.535 * 100 + 0.5); float y=floor(33000000.535 * 100 + 0.5); xx(&x); xx(&y); yy(x); yy(y); } 计算器,我们可以看到近似确实很好,但由于数学背后的数学表示存在一些扰动。

注意:我确实在bc中设置了scale=20,这意味着每个中间计算在该点之后保持20位数。

bc