printf语句中转换的百分比如何?
也许这不属于SO,但我不知道在哪里。
我必须用C
重新实现printf(3)
而不使用任何可以为我做转换的函数,我差不多完成了,但是我被困在%a
,我真的不明白这里发生了什么,例如:
printf("%a\n", 3.0); //#=> 0x1.8p+1 printf("%a\n", 3.1); //#=> 0x1.8cccccccccccdp+1 printf("%a\n", 3.2); //#=> 0x1.999999999999ap+1 printf("%a\n", 3.3); //#=> 0x1.a666666666666p+1 printf("%a\n", 3.4); //#=> 0x1.b333333333333p+1 printf("%a\n", 3.5); //#=> 0x1.cp+1 printf("%a\n", 3.6); //#=> 0x1.ccccccccccccdp+1
我当然读到了那个说:
double参数被舍入并在样式[ – ] 0xh.hhhp [+ – ] d中转换为hex表示法,其中hex点字符后面的位数等于精度规范。
但这并没有真正帮助我不理解将3.2
转换为1.999999999999ap+1
我不需要任何代码,但更多的解释。
PS:如果这不是这个问题的地方,你可以指引我到正确的地方吗?
编辑:虽然@juhist
答案适用于数字> = 1.0
但它没有解释如何获得0.0
和1.0
之间的数字的结果:
printf("%a\n", 0.01); //#=> 0x1.47ae147ae147bp-7 printf("%a\n", 0.1); //#=> 0x1.999999999999ap-4 printf("%a\n", 0.2); //#=> 0x1.999999999999ap-3 printf("%a\n", 0.3); //#=> 0x1.3333333333333p-2 printf("%a\n", 0.4); //#=> 0x1.999999999999ap-2 printf("%a\n", 0.5); //#=> 0x1p-1 printf("%a\n", 0.6); //#=> 0x1.3333333333333p-1
另外,我真的希望精确到此The "a" near the end occurs due to limited floating point calculation precision
有关转换的The "a" near the end occurs due to limited floating point calculation precision
, The "a" near the end occurs due to limited floating point calculation precision
: printf("%a\n", 3.2); //#=> 0x1.999999999999ap+1
printf("%a\n", 3.2); //#=> 0x1.999999999999ap+1
编辑2:现在最后一个谜是解释为什么在这种情况下:
printf("%a\n", 0.1); //#=> 0x1.999999999999ap-4
最后9
变为a
和在这种情况下:
printf("%a\n", 0.3); //#=> 0x1.3333333333333p-2
最后3
留3
?
首先你需要知道表示0xh.hhhh p±d,
是什么意思? 让我们通过一个hex常量0x1.99999ap+1
的例子来理解它。
小数点前的数字1
是hex数字,后面的hex数字( 99999a
)等于精度。 0x
是hex导入器, p
是指数字段。 指数是一个十进制数,表示有效部分乘以2
的幂。
因此,当0x1.99999ap+1
乘以2
1
,它将以十进制转换为3.2
。 回想一下1.55e+1
如何以十进制转换为15.500000
。 类似的事情发生在这里。
现在你需要知道0x1.99999ap+1
转换为3.2
背后的数学。 这将如下进行
1 * 16 0 + 9 * 16 -1 + 9 * 16 -2 + 9 * 16 -3 + 9 * 16 -4 + 9 * 16 -5 + 10 * 16 -1
这是(十进制)等于
1.60000002384185791015625
您只需要1个精度。 所以,取1.6
并乘以2
1
。 哪个会给3.2
。
要转到上述过程的反向,您需要找到2
的幂,浮点数将被分割以获得小数点前的数字1
。 之后使用连续乘法将小数部分更改为hex分数。 请按以下步骤操作:
- 3.2 / 2 1 = 1.6
- 从1.6中取整数部分以获得小数点前的hex数字
1
。- 乘以
.6
乘以16
。 获得的整数部分将成为hex分数中的数字。 使用获得的小数部分重复此步骤以达到所需的精度(默认值为6)。
- .6 * 16 = 9.6 —>积分= 9分数= .6
- .6 * 16 = 9.6 —>积分= 9分数= .6
- .6 * 16 = 9.6 —>积分= 9分数= .6
- .6 * 16 = 9.6 —>积分= 9分数= .6
- .6 * 16 = 9.6 —>积分= 9分数= .6
- .6 * 16 = 9.6 —>积分= 9分数= .6
因此,hex分数将变为.999999
现在组合hex指示符0x
,小数点前的hex数和与指数字段一起获得的hex分数以获得结果。
3.2 10 = 0x1.999999p + 1 16
类似地,您可以获得小于1.0
数字的hex浮点数,例如0.01
。 在这种情况下,要获得小数点前的hex数1
,您需要将其除以幂为2
。 由于乘以0.01
后的128
(2 5 )将给出其积分部分变为1
, 128*.01 = 1.28
。 这意味着你需要乘以0.01
乘1/2 – 或者你可以说你需要将它除以2 -5得到1.28
。 现在应用上述步骤2和3。
这是一个容易回答的问题。
明显的解释是(1 + 9.0 / 16 + 9.0 /(16 * 16)+9.0 /(16 * 16 * 16)+9.0 /(16 * 16 * 16 * 16)+ …)* 2 = 3.2
你可以通过五个第一个术语轻松validation这一点并将其写入Python解释器:(1 + 9.0 / 16 + 9.0 /(16 * 16)+ 9.0 /(16 * 16 * 16)+ 9.0 /(16 * 16) * 16 * 16))* 2
答案是3.199981689453125。
为什么2在最后呢? 当然,因为(1 << 1)= 2而且“p”之后的数字是+1。 如果你有+2,那么你将使用(1 << 2)= 4作为乘数。
编辑:好的,所以需要一个相反的算法。
首先,找到(3.2 /(2 ^ x))在1和2之间的偏移量x。在这种情况下,3.2 /(2 ^ 1)= 3.2 / 2 = 1.6。
然后,输出“1” 并从结果中减去1,得到0.6
然后,将结果乘以16并取整数部分。 该产品是9.6。 输出为“9”。
然后,从结果中减去输出。 9.6 – 9 = 0.6。
重复:将结果乘以16并取整数部分。 该产品是9.6。 输出为“9”。
再次,从结果中减去输出。 9.6 – 9 = 0.6
重复这些过程。 无限广告,如果你想要完全扩展,但实际上你会在某个地方停止迭代。 最后,输出“p”和“+1”,因为功率为+1。 由于有限的浮点计算精度,接近末尾的“a”发生。
另一个编辑:要完全理解有限精度会产生什么影响,你可以阅读“每个计算机科学家应该知道的关于浮点算术的内容”,这是一篇非常着名的论文,有一个在线版本,例如: http:// docs .oracle.com / CD / E19957-01 / 806-3568 / ncg_goldberg.html
数字0.1的算法示例:我们发现0.1 /(2 ^( – 4))= 0.1 * 2 ^ 4 = 0.1 * 16 = 1.6,介于1和2之间,因此我们选择功率-4。 所以,我们输出“1”。 然后计算(1.6-1)= 0.6并乘以16:16 * 0.6 = 9.6。 因此,我们输出“9”,减去9.6 – 9 = 0.6并再次乘以16,得到另一个“9”。 等等。 因此,给出无限浮点精度的答案是“1.9999999 …. p-4”。 但是,由于精度有限,最后一个字母似乎是“a”。 因此,该算法也适用于负功率,但您必须注意,除以负功率与乘以正功率相同。 因此,您还需要考虑负数,以选择给出介于1和2之间的值的幂。