[英]printing the integral part of a floating point number
我想弄清楚如何在不使用庫函數的情況下打印浮點數。 結果證明打印浮點數的小數部分非常容易。 打印組成部分更難:
static const int base = 2;
static const char hex[] = "0123456789abcdef";
void print_integral_part(float value)
{
assert(value >= 0);
char a[129]; // worst case is 128 digits for base 2 plus NUL
char * p = a + 128;
*p = 0;
do
{
int digit = fmod(value, base);
value /= base;
assert(p > a);
*--p = hex[digit];
} while (value >= 1);
printf("%s", p);
}
打印FLT_MAX
的組成部分可以完美地使用基數 2 和基數 16:
11111111111111111111111100000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000 (base 2)
ffffff00000000000000000000000000 (base 16)
但是,以基數 10 打印會導致前 7 位數字后出現錯誤:
340282368002860660002286082464244022240 (my own function)
340282346638528859811704183484516925440 (printf)
我認為這是除以 10 的結果。如果我使用 double 而不是 float 會更好:
340282346638528986604286022844204804240 (my own function)
340282346638528859811704183484516925440 (printf)
(如果您不相信printf
,請在 Wolfram Alpha 中輸入2^128-2^104
。它是正確的。)
現在, printf
如何設法打印正確的結果? 它是否在內部使用了一些 bigint 設施? 還是我缺少一些浮點技巧?
我認為問題在於value /= base;
. 不要忘記 10 不是二進制系統中的有限分數,因此這個計算永遠不會正確。 由於同樣的原因,我還假設fmod
會發生一些錯誤。
printf
將首先計算整數部分,然后將其轉換為十進制(如果我得到了正確printf
整數部分的方法)。
/編輯:首先閱讀Unni的回答。 這個結果來自http://codepad.org/TLqQzLO3 。
void print_integral_part(float value)
{
printf("input : %f\n", value);
char a[129]; // worst case is 128 digits for base 2 plus NUL
char * p = a + 128;
*p = 0;
do
{
int digit = fmod(value, base);
value /= base;
printf("interm: %f\n", value);
*--p = hex[digit];
} while (value >= 1);
printf("result: %s\n", p);
}
print_integral_part(3.40282347e+38F);
查看value /= base
操作如何弄亂您的值:
input : 340282346638528859811704183484516925440.000000
interm: 34028234663852885981170418348451692544.000000
interm: 3402823466385288480057879763104038912.000000
interm: 340282359315034876851393457419190272.000000
interm: 34028234346940236846450271659753472.000000
interm: 3402823335658820218996583884128256.000000
interm: 340282327376181848531187106054144.000000
interm: 34028232737618183051678859657216.000000
interm: 3402823225404785588136713388032.000000
interm: 340282334629736780292710989824.000000
interm: 34028231951816403862828351488.000000
interm: 3402823242405304929106264064.000000
interm: 340282336046446683592065024.000000
interm: 34028232866774907300610048.000000
interm: 3402823378911210969759744.000000
interm: 340282332126513595416576.000000
interm: 34028233212651357863936.000000
interm: 3402823276229139890176.000000
interm: 340282333252413489152.000000
interm: 34028234732616232960.000000
interm: 3402823561222553600.000000
interm: 340282356122255360.000000
interm: 34028235612225536.000000
interm: 3402823561222553.500000
interm: 340282366859673.625000
interm: 34028237357056.000000
interm: 3402823735705.600098
interm: 340282363084.799988
interm: 34028237619.200001
interm: 3402823680.000000
interm: 340282368.000000
interm: 34028236.800000
interm: 3402823.600000
interm: 340282.350000
interm: 34028.234375
interm: 3402.823438
interm: 340.282349
interm: 34.028235
interm: 3.402824
interm: 0.340282
result: 340282368002860660002286082464244022240
如有疑問,請向其扔更多的 printf ;)
根據 IEEE 單精度浮點實現,任何時候都只能在浮點變量中存儲 24 位數據。 這意味着浮點數中最多只能存儲 7 位十進制數字。
數字的其余部分存儲在指數中。 FLT_MAX 被初始化為 3.402823466e+38F。 因此,在第 10 個精度之后,沒有在任何地方定義應該打印哪個數字。
從 Visual C++ 2010 編譯器中,我得到這個輸出 340282346638528860000000000000000000000.000000,這是唯一有效的輸出。
所以,最初我們有這么多有效數字 3402823466 所以在第一次除法之后我們只有 0402823466 所以,系統需要去掉左邊的 0 並在右邊引入一個新數字。 在理想的整數除法中,它是 0。因為您正在執行浮動除法 (value /= base;) ,系統正在獲取一些其他數字來填充該位置。
因此,在我看來, printf 可以將上述可用有效數字分配給一個整數並使用它。
似乎浮點到字符串轉換的工作馬是dtoa()
函數。 請參閱 newlib 中的dtoa.c了解他們是如何做到的。
現在, printf 如何設法打印正確的結果?
我認為它接近魔術。 至少源頭看起來像是某種黑暗的咒語。
它是否在內部使用了一些 bigint 設施?
是的,在鏈接的源文件中搜索_Bigint
。
還是我缺少一些浮點技巧?
可能。
讓我們再解釋一次。 在整數部分被打印(完全)之后,除了向 0 切碎之外沒有任何舍入,是十進制位的時候了。
從包含二進制零的一串字節(對於初學者來說是 100)開始。 如果設置了 fp 值中小數點右側的第一位,則表示 0.5(2^-1 或 1/(2^1) 是分數的一個組成部分。因此將 5 添加到第一個字節。如果下一位設置為 0.25 (2^-2 或 1/(2^2)) 是分數的一部分 將 5 添加到第二個字節並將 2 添加到第一個字節(哦,不要忘記進位,它們發生了 -低年級數學)。下一位設置的意思是 0.125,所以第三個字節加 5,第二個字節加 2,第一個字節加 1。依此類推:
value string of binary 0s
start 0 0000000000000000000 ...
bit 1 0.5 5000000000000000000 ...
bit 2 0.25 7500000000000000000 ...
bit 3 0.125 8750000000000000000 ...
bit 4 0.0625 9375000000000000000 ...
bit 5 0.03125 9687500000000000000 ...
bit 6 0.015625 9843750000000000000 ...
bit 7 0.0078125 9921875000000000000 ...
bit 8 0.00390625 9960937500000000000 ...
bit 9 0.001953125 9980468750000000000 ...
...
我是手工完成的,所以我可能錯過了一些東西,但在代碼中實現它是微不足道的。
因此,對於所有那些“無法使用浮點數獲得確切結果”的人來說,不知道他們在說什么的人證明了浮點分數值是完全准確的。 極其准確。 但是二進制。
對於那些花時間了解其工作原理的人來說,更高的精度是觸手可及的。 至於其他人......好吧,我想他們會繼續不瀏覽論壇來回答一個以前已經回答過多次的問題,老實說,他們相信他們已經發現了“破碎的浮點”(或者隨便你怎么稱呼它)並每天發布同一問題的新變體。
“接近魔法”,“黑暗咒語”——太搞笑了!
就像Agent_L的答案一樣,你會遇到因將值除以10而導致的錯誤結果.Float與任何二進制浮點類型一樣,無法正確表達十進制中的最有理數。 除法之后,大多數情況下結果都不能擬合成二進制,所以它將被舍入。 因此,你划分越多,你就會意識到越多的錯誤。
如果數字不是很大,快速解決方案是將其乘以10或10的冪,具體取決於您需要的小數點后的位數。
另一種方式在這里描述
這個程序會為你工作。
#include<stdio.h>
int main()
{
float num;
int z;
scanf("%f",&num);
z=(int)num;
printf("the integral part of the floating point number is %d",z);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.