百科问答小站 logo
百科问答小站 font logo



一个浮点数到底是怎么被转换为字符串输出?一个浮点数不精确,那么其输出的值是怎么被确定的呢? 第1页

  

user avatar   be5invis 网友的相关建议: 
      

浮点数的二进制表示是精确的,比如 0.3 的最接近二进制表示是 00111110100110011001100110011010。

因此,“打印浮点数”这个问题实际上是:寻找一个最短的十进制数,使得它的最接近二进制表示和给定的二进制相同。

目前这方面最好的算法是 Ryu,可参考这里:github.com/ulfjack/ryu


user avatar   xing-jiankuan 网友的相关建议: 
      

系统存储和输出浮点数时,是没有“精确值“的概念的。它的确会按照IEEE754的方式“趋近”。下文详细讲讲。

这里从Java开始讲解(但其实这是个普遍的问题,不限于Java)。Java中println(一个浮点数)实际使用的是Float.toSring(float f)。其文档如下。我把关于位数的说明用红框标注。

英文有点绕,怎么理解呢?我画了个图说明下这个问题。(留意下下这个图比例上并不精确,大致说明一下概念而已)

图中数轴下面表示精确的数学上的值。上面则是float的表示,包括就是IEEE754的Hex表示,和程序对这个float的输出数值。如果你还不理解IEEE754是啥,你可以大致理解为,一个浮点数表示为:

其中mantissa表示有效数字,exponent是2的幂,sign是表示正负数的符号。对于数字精度,最关键的是mantissa。

每个float的最小精度的变动是其mantissa部分加1或者减1。因为mantissa长度有限(float为23个bit),每个float实际上表示的是数学上一个范围(如上图)在这个范围内,无法再精细的分辨。比如,没法精确的区分0.79999995和0.79999996——他们的IEEE754 Hex表示都是0x3f4ccccc。对mantissa加1或者减1都会跳到隔壁的“范围”里

每个范围都会有一个对应的数值,就是通过上面的公式计算的结果。比如,0x3f4ccccc的值是“1.5999999046325684 * 2^(-1) = 0.7999999523162841796875“。这个值可以唯一的确定一个“范围”。值得注意的是,对于每一个浮点数,可以找到一个最小的位数n,使得这个值round到n后,依然可以和隔壁的范围不冲突。这也就是上面文档中

... but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of typefloat...

要表达的意思。

例如,对于0x3f4ccccc,这个round的数值就是0.79999995。如果再少保留一位,把末尾的5干掉,四舍五入后就是0.8了,这就跟0x3f4ccccd一样,分不开了。

上图中给出了3个float的“round值“。对于0x3f4ccccc,值就是“0.79999995”;下一个是0x3f4ccccd,这个值就是“0.8”,再下一个是0x3f4cccce,值是“0.8000001”。

当你在程序中输入了一个浮点数X,再输出得到Y,就会经历以下过程。1)程序编译器/解释器会按照IEEE754的方式存储那个数字;2)当你输出时,程序就会按照IEEE754计算值的公式算出对应的“值”,并且round到足够能和相邻浮点数分辨的位数,得到一个字符串表示,也就是Y。

比如你写"float a = 0.8f",程序解析为“用float格式存储0.8,保存到变量a的内存地址上“,他的IEEE754 Hex表示是0x3f4ccccd,其值就是0.800000011920928955078125,最小能分辨的值就是0.8。请留意:虽然你代码写的是0.8,输出的也是0.8,但是这俩0.8不是一回事。比如,如果你输入的是0.799999999,得到的IEEE754表示依然是0x3f4ccccd,输出的还是0.8。

这个“可以最小分辨的值的字符串表示”就是Float.toString(float f)返回的结果,也就是我们常规看到的printf(float f)打印到控制台的结果。

顺便说说另外一个例子:

对于0.8f + 0.1f,其IEEE754的Hex表示为“0x3f666667“, 最小可分辨值是"0.90000004"。

对于0.9f,其IEEE754的Hex表示为“0x3f666666“,最小可分辨值刚好为"0.9"。

所以你可以留意到println(0.8f + 0.1f)与println(0.9f)的输出会不一样。

so,那个该死的把浮点数输出的算法到底是什么?这个问题比表面上看起来要困难得多。可以参考这个系列文章:

或者直接从这篇论文开始:




  

相关话题

  既然有文件后缀名,为何还需要MIME类型? 
  打字速度对编程的影响大吗? 
  Java的语法规则是怎么传播开来的?(如若回答,请看完补充说明)? 
  程序员行业有哪些越早知道越好的道理? 
  如何评价单片机大神郭天祥? 
  有什么编程的好习惯建议吗? 
  用JavaScript做其他语言擅长的事情“好”么? 
  高中就已经写过 10w 行代码的人都是什么人? 
  Java、C#、.NET Framework、Mono 是如何跨平台的? 
  有没有工具能找出程序01代码相同的部分, 精简掉相同部分,只操作他们的地址码来让程序依然能运行? 

前一个讨论
人眼是单透镜(晶状体),为什么我们看到的景象没有畸变与紫边?照相机的感光件为什么不设计成视网膜的球形?
下一个讨论
如何看待腾讯视频关于利奇马台风伤亡人数的推送文案出错,写成全省人死亡,并后续道歉一事?





© 2024-05-03 - tinynew.org. All Rights Reserved.
© 2024-05-03 - tinynew.org. 保留所有权利