繁体   English   中英

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

[英]Why is the function floor giving different results in this case?

在此示例中, 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?

C中的double并不代表可以用文本表示的所有可能数字。

double通常可以表示大约2 64个不同的数字。 double编码为二进制浮点数时,该集合中都不存在34000000.53533000000.535 而是使用最接近的可表示数字。

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精度数。 因此,代码的结果可能会有所不同。

问题已经在这里回答

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

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

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

唯一可以保证的是ab相对较小。

使用将内存中的浮点数表示为项总和的代码 ,我们得到:

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);
}

在两种情况下,此代码都将在输出中存储floor所返回值的表示形式。

使用bc calcultor,我们可以看到近似值确实不错,但由于发言权表示背后的数学原因,存在一些干扰。

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

./a.out
1ST NUMBER=>    sign:0 exponent:1 0 0 1 1 1 1 fraction:0 1 0 0 1 0 1 0 1 0 1 0 0 1 1 1 1 1 1 0 0 0 1 0
2ND NUMBER=>    sign:0 exponent:1 0 0 1 1 1 1 fraction:0 1 0 0 0 1 0 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0 0 1
1ST NUMBER=>    positive ( 1+1/(2) +1/(16) +1/(64) +1/(256) +1/(1024) +1/(8192) +1/(16384) +1/(32768) +1/(65536) +1/(131072) +1/(262144) +1/(4194304) )*2^31
2ND NUMBER=>    positive ( 1+1/(2) +1/(32) +1/(256) +1/(1024) +1/(2048) +1/(16384) +1/(8388608) )*2^31
@ bc
scale=20
( 1+1/(2) +1/(16) +1/(64) +1/(256) +1/(1024) +1/(8192) +1/(16384) +1/(32768) +1/(65536) +1/(131072) +1/(262144) +1/(4194304) )*2^31
3399999999.99999999999463129088
 ( 1+1/(2) +1/(32) +1/(256) +1/(1024) +1/(2048) +1/(16384) +1/(8388608) )*2^31
3299999999.99999999999731564544

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM