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.01.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 precisionThe "a" near the end occurs due to limited floating point calculation precisionprintf("%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 

最后33

首先你需要知道表示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分数。 请按以下步骤操作:

  1. 3.2 / 2 1 = 1.6
  2. 从1.6中取整数部分以获得小数点前的hex数字1
  3. 乘以.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 )将给出其积分部分变为1128*.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之间的值的幂。