簡體   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