C printf %a 和 %La
C printf %a and %La
我必须使用 C
重新实现 printf(3)
,而不使用任何可以为我进行转换的函数。
我以为我理解了谢谢大家 %a
是如何工作的:
然后我意识到我不明白rounding
是如何完成的所以我问:然后以为在你帮助我之后我完成了。
现在 %a
完全像官方的一样工作,我认为 %La
会和 %a
完全一样,但是 long double
因为那个人只说:
Modifier a, A, e, E, f, F, g, G
l (ell) double (ignored, same behavior as without it)
L long double
我发现它输出的东西完全不同:'(
double a_double = 0.0001;
long double a_long_double = 0.0001;
printf("%a\n", a_double); #=> 0x1.a36e2eb1c432dp-14
printf("%La\n", a_long_double); #=> 0xd.1b71758e21968p-17
%a
结果总是以 1.
开头,现在我完全不明白 %La
在做什么。
你能帮我理解将 0.0001
转换为 0xd.1b71758e21968p-17
的 过程 吗?
编辑:我真正不明白的部分是为什么 %a
总是输出以1.
而不是[=16=开头的东西] ?
EDIT2:更准确地说:为什么 %a
选择输出 1. ...p-14
而 %La
选择输出 d. ...p-17
?
不清楚结果的哪一方面让您感到惊讶。如果是指数和数字的差异,那只是因为归一化不佳。请注意,对于第一个结果,您在小数点之前得到一个 1
,而对于第二个结果,您在它之前得到一个 d
。将 d
右移 3 位,得到 1
,低位 (101) 移到小数点后的下一个位置,如预期的那样得到 a
。
至于0.0001
如何转化为0xd.1b71758e21968p-17
,无非是找到最接近十进制数0.0001的二进制浮点数。如何有效完成这项工作的机制有点复杂,但概念很简单。
任何(有限的,非零的)浮点数都有四个不同的(有效的)十六进制浮点表示:
- 最大的十六进制数在8和F之间
- 另一个最大的十六进制数在4到7之间
- 另一个最高十六进制数是2或3
- 另一个最高十六进制数是1
并且它们具有连续递增的指数值。您可以使用右移从一个传递到另一个;这个过程可能会向右增加另一个数字; C 标准中没有关于应该使用哪种表示形式进行 %a
转换的限制。
使用 IEEE float
(24 位有效数)或通常的双扩展(64 位有效数,与 i386 Linux 上的 long double 一样),因为位数可以被整除4、习惯上对归一化数使用第一种形式,因为它是用最少的十六进制数来完整表示数的形式(分别是十六进制后的5和15位)点.
).
对于 IEEE double
(53 位有效数),另一方面,在点 .
之后使用第四种形式和 13 个十六进制数字看起来更好:因此所有显示的数字实际上代表数据。 IEEE quad(正式为 binary128
,113 位有效数)也会发生同样的情况,点后有 28 个十六进制数字。
这些表示也有很好的 属性 来匹配数字的内存表示(这就是为什么 C99 标准中的脚注指导实现这样做的原因。)
如果我们现在查看您的问题,第一个示例 (double
) 符合上述准则,第一个数字是 1,点后是 13 个十六进制数字。
第二个例子比较狡猾。首先,一个 double
常量被存储到一个 long double
变量中:根据编译器的不同,常量可能会四舍五入到 double
的精度(就像这里所做的那样。)这意味着第 53 位之后的位将全为零。然后使用%La
转换,同样按照上面的指导方针,因此选择第一种表示,以D开头(二进制1101xxx,与转换为11010xxx的1.A进行比较);这里也只有 13 个十六进制数字,因为编译器丢弃了打印不必要的零(由于四舍五入。)
请注意,您不能将 printf
的 %a
转换用于 float
。
你没有说你在这里工作的机器是什么类型,但从你得到 0xd.
... 结果 %La
的事实来看,这意味着这是一台机器long double
是一种 非规范化 浮点类型(例如 8087 上的 80 位浮点数)。使用 标准化 浮点数,您将始终得到以 0x1.
... ,这就是您在这里看到的。
%a
转换规范的全部要点是允许将二进制浮点数打印为文本,以便稍后可以使用 scanf 读回,这将导致位相同的值在具有相同浮点表示的机器上。
我必须使用 C
重新实现 printf(3)
,而不使用任何可以为我进行转换的函数。
我以为我理解了谢谢大家 %a
是如何工作的:
然后我意识到我不明白rounding
是如何完成的所以我问:
现在 %a
完全像官方的一样工作,我认为 %La
会和 %a
完全一样,但是 long double
因为那个人只说:
Modifier a, A, e, E, f, F, g, G
l (ell) double (ignored, same behavior as without it)
L long double
我发现它输出的东西完全不同:'(
double a_double = 0.0001;
long double a_long_double = 0.0001;
printf("%a\n", a_double); #=> 0x1.a36e2eb1c432dp-14
printf("%La\n", a_long_double); #=> 0xd.1b71758e21968p-17
%a
结果总是以 1.
开头,现在我完全不明白 %La
在做什么。
你能帮我理解将 0.0001
转换为 0xd.1b71758e21968p-17
的 过程 吗?
编辑:我真正不明白的部分是为什么 %a
总是输出以1.
而不是[=16=开头的东西] ?
EDIT2:更准确地说:为什么 %a
选择输出 1. ...p-14
而 %La
选择输出 d. ...p-17
?
不清楚结果的哪一方面让您感到惊讶。如果是指数和数字的差异,那只是因为归一化不佳。请注意,对于第一个结果,您在小数点之前得到一个 1
,而对于第二个结果,您在它之前得到一个 d
。将 d
右移 3 位,得到 1
,低位 (101) 移到小数点后的下一个位置,如预期的那样得到 a
。
至于0.0001
如何转化为0xd.1b71758e21968p-17
,无非是找到最接近十进制数0.0001的二进制浮点数。如何有效完成这项工作的机制有点复杂,但概念很简单。
任何(有限的,非零的)浮点数都有四个不同的(有效的)十六进制浮点表示:
- 最大的十六进制数在8和F之间
- 另一个最大的十六进制数在4到7之间
- 另一个最高十六进制数是2或3
- 另一个最高十六进制数是1
并且它们具有连续递增的指数值。您可以使用右移从一个传递到另一个;这个过程可能会向右增加另一个数字; C 标准中没有关于应该使用哪种表示形式进行 %a
转换的限制。
使用 IEEE float
(24 位有效数)或通常的双扩展(64 位有效数,与 i386 Linux 上的 long double 一样),因为位数可以被整除4、习惯上对归一化数使用第一种形式,因为它是用最少的十六进制数来完整表示数的形式(分别是十六进制后的5和15位)点.
).
对于 IEEE double
(53 位有效数),另一方面,在点 .
之后使用第四种形式和 13 个十六进制数字看起来更好:因此所有显示的数字实际上代表数据。 IEEE quad(正式为 binary128
,113 位有效数)也会发生同样的情况,点后有 28 个十六进制数字。
这些表示也有很好的 属性 来匹配数字的内存表示(这就是为什么 C99 标准中的脚注指导实现这样做的原因。)
如果我们现在查看您的问题,第一个示例 (double
) 符合上述准则,第一个数字是 1,点后是 13 个十六进制数字。
第二个例子比较狡猾。首先,一个 double
常量被存储到一个 long double
变量中:根据编译器的不同,常量可能会四舍五入到 double
的精度(就像这里所做的那样。)这意味着第 53 位之后的位将全为零。然后使用%La
转换,同样按照上面的指导方针,因此选择第一种表示,以D开头(二进制1101xxx,与转换为11010xxx的1.A进行比较);这里也只有 13 个十六进制数字,因为编译器丢弃了打印不必要的零(由于四舍五入。)
请注意,您不能将 printf
的 %a
转换用于 float
。
你没有说你在这里工作的机器是什么类型,但从你得到 0xd.
... 结果 %La
的事实来看,这意味着这是一台机器long double
是一种 非规范化 浮点类型(例如 8087 上的 80 位浮点数)。使用 标准化 浮点数,您将始终得到以 0x1.
... ,这就是您在这里看到的。
%a
转换规范的全部要点是允许将二进制浮点数打印为文本,以便稍后可以使用 scanf 读回,这将导致位相同的值在具有相同浮点表示的机器上。