打印浮点数的整数部分

我试图弄清楚如何在不使用库函数的情况下打印浮点数。 打印浮点数的小数部分结果非常简单。 打印整体部件更难:

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 ,请输入2^128-2^104到Wolfram Alpha。这是正确的。)

现在, printf如何设法打印正确的结果? 它在内部使用一些bigint设施吗? 或者是否有一些我失踪的浮点技巧?

似乎浮动到字符串转换的工作马是dtoa()函数。 请参阅newlib中的dtoa.c ,了解它们是如何做到的。

现在,printf如何设法打印正确的结果?

我认为它接近魔术。 至少来源看起来像某种黑暗的咒语。

它在内部使用一些bigint设施吗?

是的,在链接的源文件中搜索_Bigint

或者是否有一些我失踪的浮点技巧?

有可能。

我认为问题在于价值/ =基数; 不要忘记10在二元系统中不是有限分数,因此这种计算永远不正确。 我还假设由于同样的原因在fmod中会发生一些错误。

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 

如有疑问,请向其投掷更多printfs;)

根据IEEE单精度浮点实现,只有24位数据随时存储在float变量中。 这意味着浮动数中只存储最多7位小数。

数字的其余部分存储在指数中。 FLT_MAX初始化为3.402823466e + 38F。 因此,在第10个精度之后,哪个数字应该打印在任何地方都没有定义。

从Visual C ++ 2010编译器,我得到此输出340282346638528860000000000000000000000.000000,这是唯一的vaild输出。

所以,最初我们有这么多有效数字3402823466所以在第一次分裂后我们只有0402823466所以,系统需要摆脱左边0并在右边引入一个新数字。 在理想的整数除法中,它是0.因为你正在进行浮动除法(值/ =基数;),系统正在获得一些其他数字来填充该位置。

所以,在我看来,printf可能会将上述可用的有效数字分配给一个整数并使用它。

让我们再解释一次。 在完全打印整数部分之后,没有任何舍入而不是向0的斩波,这是小数位的时间。

从包含二进制零的字节串(例如100表示​​启动器)开始。 如果设置了fp值中小数点右边的第一位,则意味着0.5(2 ^ -1或1 /(2 ^ 1)是分数的一个分量。所以在第一个字节中加5。设置下一位0.25(2 ^ -2或1 /(2 ^ 2))是第二个字节加5的分数的一部分,并将第2个加到第2个(哦,不要忘记进位,它们发生 – 低学校数学。)下一位设置意味着0.125,所以在第三个字节加2,第二个加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 ... ... 

我是手工完成的,所以我可能错过了一些东西,但是在代码中实现它是微不足道的。

因此对于所有那些“无法使用浮点数获得精确结果”的人来说,不知道他们在这里谈论什么的人certificate了浮点分数值是完全精确的。 令人难以置信的确切。 但二进制。

对于那些花时间了解其工作原理的人来说,更好的精确度是可以实现的。 至于其他人……好吧我猜他们会继续不去浏览这个问题的答案,而这个问题已经多次被回答了,老实说相信他们已经发现了“破碎的浮动点”(或者无论如何称之为)并且每天发布相同问题的新变体。

“接近魔法”,“黑暗咒语” – 这很有趣!

就像Agent_L的回答一样,你会遇到因将值除以10而导致的错误结果。浮点数与任何二进制浮点类型一样,无法正确表达十进制的最有理数。 除法之后,大多数情况下结果都不能拟合成二进制,所以它将被舍入。 因此,你划分得越多,你就会意识到越多的错误。

如果数字不是很大,快速解决方案是将其乘以10或10的幂,具体取决于您需要的小数点后的位数。

另一种方式在这里描述

这个程序适合你。

 #include int main() { float num; int z; scanf("%f",&num); z=(int)num; printf("the integral part of the floating point number is %d",z); }