繁体   English   中英

为什么以及如何,这个C程序准确显示7.21?

[英]Why and How, is this C program showing 7.21 accurately?

我很熟悉这样的事实:十进制分数通常不会整齐地转换为二进制,因此存储近似值,并且当转换回十进制时与原始数字相差一点。 我相信0.21就是这样一个分数的一个例子。

如何以及为什么这个程序在gcc中准确编译显示7.21?

而且(这个问题的重要第二部分),为什么它准确显示7.21,它是否显示839.21不准确?

我可以理解为什么它会错误地显示0.21,或者不正确地显示任何数字21,因为它需要很多位来准确地将它放入二进制,如果有的话。 但我希望它始终显示不准确显示n.21无论整数n是多少

printf("%.100f\n", 7.21);  
printf("%.100f\n", 839.21); 
produces 7.210000000000000000000... 
839.2100000000000400000....

知道为什么它准确地做了一个而且一个不准确吗? (无论如何,在我的gcc编译器上!)

如果相关,gcc --version显示gcc.exe (rubenvb-4.5.4) 4.5.4 Copyright (C) 2010 Free Software Foundation, Inc.

我跟一个人说过,他说两者都不准确,这更像是人们所期待的......所以我拍了一个屏幕显示行为

在此输入图像描述

注意 - 我注意到我的cygwin gcc实现不是最新的cygwin gcc实现..我只是where gcc运行它从C:\\Perl64\\site\\bin\\gcc.exe运行它所以它没有运行cygwin。 它可能正在运行一个老明星(这是R建议的)。 chux是cygwin n GNU C11(GCC)版本6.4.0(i686-pc-cygwin)`。

如何以及为什么这个程序在gcc中准确编译显示7.21?

处理最低有效十进制数字的旧printf()是有限的。

printf("%.100f\\n", x); 不需要打印x准确地通过一定数字。 确实没有C规范,但十进制输出应该至少DBL_DIG数字(通常为15)是正确的 - 这将是“弱”并且有问题。

对于至少DBL_DECIMAL_DIG数字(通常为17),更好且通常可接受的printf()将是正确的。 没有无限制的精确度,最后一个数字是正确的可能是麻烦的。 见表制造者的困境 用零填充“右”而不是正确的数字并不罕见。 这就是OP的代码所做的 它转到正确的圆形17位数,然后填零。

高质量的printf()将打印double x = 839.21; 并且x = 7.21; 正确的所有数字。 例如:

839.2100000000000363797880709171295166015625...
839.210000000000000019984014443252817727625370025634765625.... (as long double)
vs OP's
839.2100000000000400000....

123.4567890123456789 // digit place

7.20999999999999996447286321199499070644378662109375...
7.210000000000000000034694469519536141888238489627838134765625000... (as long double)
7.210000000000000000000

OP的printf()最多可达16个左右。

7.210000000000000000000....的输出看起来很棒,因为printf()走到一个点然后用零填充。 @Eric Postpischil运气


注:某些优化将执行与任务long double (研究FLT_EVAL_METHOD ),以便long double86扩展精度结果张贴过。

由Barlop添加

Eric Postpischil的其他一些观点

OP的printf并不精确到7.21。 OP的printf正好通过7.20999999999999996447286321199499070644378662109375,但它打印了7.210 ......,所以这是错误的。 它碰巧是错误的,因为OP的printf错误确切地抵消了7.21被舍入到二进制浮点时发生的错误。

Eric的正确,printf(“%。100f \\ n”,7.20999999999999996447286321199499070644378662109375); 打印7.210000000

埃里克详细阐述了他是如何知道它是7.20999999999999996447286321199499070644378662109375,它被发送到printf而不是其他一些接近它的长号。

Eric评论说,他知道这一点,通过使用一个好的C实现,可以将源代码中的十进制数字正确转换为二进制浮点并且可以正确打印。 (Apple在macOS上的开发人员工具。)并使用他编写的一些Maple(数学软件)代码来帮助他进行浮点运算。 在没有这些的情况下,他可能不得不长时间地解决这个问题,就像在小学一样,但有更多的数字。

我会接受接受的答案。

但我会回答这个问题,以解决有关839.21的问题和问题的其他部分

在cygwin gcc实现的printf中似乎存在一个错误。 虽然R认为这是一件很明智的事情,并且有一个类似于与mingw相似的printf bug,这是一个Windows C / C ++运行时文件的错误。 虽然Chux发现他的cygwin实现很好,但他的cygwin实现更新。

我找到了一个没有bug的在线C编译器,所以它很好地说明了这一点。 https://www.tutorialspoint.com/compile_c_online.php

我的问题之一是为什么7.21准确显示和839.21显示不准确,因为它们在小数点后有相同的分数。

printf(“%。100f \\ n”,7.21);
printf(“%。100f \\ n”,839.21);

值得一看没有bug的printf。

printf("%f\\n",7.21); // 7.210000

printf("%f\\n",839.21); // 839.210000

我们可以看到它们是如何存储在double中的

printf("%.60f\n",7.21);

7.209999999999999964472863211994990706443786621093750000000000


printf("%.20a\n",7.21);  

0x1.cd70a3d70a3d70000000p+2


printf("%.60f\n",839.21);

839.210000000000036379788070917129516601562500000000000000000000


printf("%.20a\n",839.21);

0x1.a39ae147ae1480000000p+9

请注意,以二进制形式存储的分数(即二进制点之后的数字)是非常不同的,对于7.21而言与839.21非常不同。这是因为在科学记数法中,在该点之前只有一个数字。 因此,即使它只是科学记数法中的十进制数,分数部分也会不同。 7.21和8.3921如果你看二进制的839它是1101000111所以你可以看到1.A那里,因为第一位是1,然后对于下面的半字节你有1010是A.不同于7.21的情况

所以这就是为什么人们不应该期望在准确性方面得到相同的结果,存储7.21和839.21

Chux提到我的实现中存在一个问题,涉及在%a情况下内部转换为float。 因此,通过执行float f;值得研究它们如何存储在浮点数中float f; 并使用printf的上述工作实现传递给格式说明符为%.60f和%.20a的printf。

printf("%.60f\n",f); //float f=7.21  
7.210000038146972656250000000000000000000000000000000000000000

printf("%.20a\n",f); //float f=7.21  
0x1.cd70a400000000000000p+2 <-- 7.21 in float, in hex.

printf("%.60f\n",f); //float f=839.21  
839.210021972656250000000000000000000000000000000000000000000000
0x1.a39ae200000000000000p+9 <-- 839.21  in float, in hex

现在,如果我们看一下我的问题,Chux注意到有一个错误,它将数字转换为浮动的%a情况。

所以例如在我的早期(不是最新的)cygwin实现中,这是我的问题以及错误在哪里, double x=7.21; printf("%a\\n", x); double x=7.21; printf("%a\\n", x); 正在打印十六进制,但它显示存储为浮点数的十六进制。 0x1.cd70a4p+2

可能还有更多的bug,因为%f的情况可能有点不同,但无论如何,绝对是一个printf bug,在早期的cygwin实现中gcc.exe(rubenvb-4.5.4)4.5.4版权所有(C)2010自由软件基金会(自由软件基金会,公司是否会影响早期的mingw,我也不知道)。

这里有一个与mingw的printf相关的printf错误,这个错误归结为C / C ++运行时文件。 在MinGW上使用iostreams错误地打印了long double但是我的问题在chux使用的最新cygwin gcc中解决了。 (我会尝试最新的cygwin来仔细检查)。

暂无
暂无

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

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