[英]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.