为了账号安全,请及时绑定邮箱和手机立即绑定

当'%b'动词是浮数时,它的输出是什么

当'%b'动词是浮数时,它的输出是什么

Go
眼眸繁星 2022-09-19 20:47:36
根据go文档,与浮数一起使用意味着:%b无十进制的科学记数法,指数为2的幂,以strconv的方式。格式浮动为“b”格式,例如 -123456p-78如下代码所示,程序输出为8444249301319680P-51我对浮数有点困惑,谁能告诉我这个结果是如何计算的?这是什么意思?%bp-f := 3.75fmt.Printf("%b\n", f)fmt.Println(strconv.FormatFloat(f, 'b', -1, 64))
查看完整描述

3 回答

?
白板的微信

TA贡献1883条经验 获得超3个赞

这意味着:decimalless scientific notation with exponent a power of two

8444249301319680*(2^-51) = 3.75 or 8444249301319680/(2^51) = 3.75

p-51均值,也可以计算为2^-511/(2^51)

关于浮点算术的好文章。https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html


查看完整回答
反对 回复 2022-09-19
?
拉莫斯之舞

TA贡献1820条经验 获得超10个赞

值得指出的是,由于浮点数的内部存储格式,运行时系统也特别容易生成输出。%b

如果我们忽略“非规范化”的浮点数(我们可以稍后将它们加回来),浮点数在内部存储为1.bbbbbb...对于某些位集(此处为“b”),bbb x 2exp,例如,值 4 存储为 。值 6 存储为 ,值 7 存储为 ,八 存储为 。值七分半是,七和四分之三是,依此类推。这里的每个位,在1.bbbb中,代表低于指数的2的下一个幂。1.000...000 <exp> 21.100...000 <exp> 21.110...000 <exp> 21.000...000 <exp> 31.111 <exp> 21.1111 <exp> 2

要使用格式打印出来,我们只需注意,我们需要连续四位,即值15十进制或0xf或二进制,这导致指数需要减少3,因此我们希望乘以2-1或1/2,而不是乘以22或4。因此,我们可以取实际的指数(2),减去3(因为我们移动“点”三次以打印二进制或15),从而打印出字符串 。1.111 <exp> 2%b11111111115p-1

不过,这不是Go的印刷品:它打印 。这是相同的值(所以任何一个都是正确的输出)——但为什么呢?%b8444249301319680p-50

好吧,是,在十六进制中,.扩展到完整的二进制,这是 。这是53个二进制数字。为什么53个二进制数字,而四个就足够了?84442493013196801E0000000000001 1110 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

答案可以在Nick的答案中的链接中找到:IEEE 754浮点格式使用53位的“尾数”或“有效数”(后者是更好的术语,也是我通常尝试使用的术语,但你会看到前者经常弹出)。也就是说,有52 s,加上那个强行引入的领先者。因此,始终有 53 个二进制数字(对于 IEEE“双精度”)。1.bbb...bbbb1

如果我们只是把这个53进制数字视为十进制数,我们总是可以在没有小数点的情况下打印出来。这意味着我们只需调整两个指数的幂。

在IEEE754格式中,指数本身已经以“多余的形式”存储,并添加了1023(再次用于双精度)。这意味着它实际上以指数值为2 + 1023 = 1025存储。这意味着,为了获得2的实际幂,格式化数字的机器代码已经必须减去1023。我们可以让它同时再减去52。1.111000...000 <exp> 2

最后,由于隐含的始终存在,因此内部IEEE754编号实际上并不存储该位。因此,要读出值并对其进行转换,代码在内部执行以下操作:11

decimalPart := machineDependentReinterpretation1(&doubleprec_value)
expPart := machineDependentReinterpretation2(&doubleprec_value)

其中,依赖于机器的重新解释只是提取正确的位,根据需要在小数部分放入隐含位,减去指数部分的偏移量(1023 + 52),然后执行以下操作:1

fmt.Sprint("%dp%d", decimalPart, expPart)

当以十进制打印浮点数时,基数转换(从基数 2 到基数 10)是有问题的,需要大量的代码才能正确舍入。像这样以二进制格式打印它要容易得多。

为读者提供的练习,以帮助理解这一点:

  1. 计算 1.102 x 22。注:1.12 为十进制 11/2。

  2. 计算 11.02 x 21。(11.02 是 3。

  3. 基于上述内容,当您左右“滑动二进制点”时会发生什么?

  4. (更难)为什么我们可以假设一个领先的1?如有必要,请继续阅读。

为什么我们可以假设一个领先的1?

让我们首先注意,当我们使用十进制的科学记数法时,我们不能假设一个前导。数字可能是 1.7 x 103,或 5.1 x 105,或者其他什么。但是,当我们“正确”使用科学记数法时,第一个数字永远不会为。也就是说,我们不写0.3 x 100,而是写3.0 x 10-1。在这种表示法中,位数告诉我们精度,第一个数字永远不必为零,通常也不应该是零。如果第一个数字为零,我们只需移动小数点并调整指数(请参阅上面的练习 1 和 2)。1

同样的规则也适用于浮点数。例如,我们不存储,而是将二进制点滑动到两个位置上并得到,并将指数减小2。如果我们可能想要存储,我们将二进制点向另一个位置滑动并增加指数。每当我们这样做时,第一个数字最终总是一个0.011.0011.1

这里有一个很大的例外,那就是:当我们这样做时,我们不能存储零!因此,我们不会对数字这样做。在 IEEE754 中,我们存储为全零位(符号除外,我们可以将其设置为存储 )。这有一个全零指数,计算机硬件将其作为特殊情况处理。0.00.0-0.0

非规范化数字:当我们不能假设前导 1 时

这个系统有一个明显的缺陷(不能完全通过去规范来解决,但尽管如此,IEEE也有去规范)。也就是说:我们可以存储的最小数字“突然下溢”为零。Kahan有一个关于逐渐下溢的15页“简短教程”,我不打算试图总结,但是当我们达到最小允许指数(2-1023)并希望“变小”时,IEEE让我们停止使用这些带有前导位的“规范化”数字。1

这不会影响 Go 本身格式化浮点数的方式,因为 Go 只是“按原样”获取整个有效数。我们所要做的就是停止插入253“隐含1”,当输入值是非规范化的数字时,其他一切都只是工作。我们可以将这种魔力隐藏在依赖于机器的重新解释代码中,或者在Go中显式执行,以更方便的方式为准。float64


查看完整回答
反对 回复 2022-09-19
?
慕容708150

TA贡献1831条经验 获得超4个赞

科学记数法的五条规则如下:

  1. 基数始终为 10

  2. 指数必须是非零整数,这意味着它可以是正数或负数

  3. 系数的绝对值大于或等于 1,但应小于 10

  4. 系数带有符号 (+) 或 (-)

  5. 曼蒂萨携带其余的有效数字

p

  • %b指数为2的幂的科学记数法(其p)

  • %e科学记数法


查看完整回答
反对 回复 2022-09-19
  • 3 回答
  • 0 关注
  • 117 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号