java浮点数精度丢失,如何最小化数值误差

IEEE 754是美国电气电子工程师协会通过地一个标准,用于在计算机上表示浮点数。Java采用32位IEEE 754表示float型,64位IEEE 754表示doubl型。 IEEE 标准中,浮点数是将特定长度的连续字节的所有二进制位分割为特定宽度的符号域,指数域和尾数域三个域,其中保存的值分别用于表示给定二进制浮点数中的符号,指数和尾数。这样,通过尾数和可以调节的指数(所以称为"浮点")就可以表达给定的数值了,比如 123.45 用十进制科学计数法可以表达为 1.2345 × 102 ,其中 1.2345 为尾数,10 为基数,为指数。浮点数利用指数达到了浮动小数点的效果,从而可以灵活地表达更大范围的实数。

具体的格式参见下面的图例:


    float和double类型在java中执行地是二进制浮点运算,这是为了在广泛的数值范围上提供较为精确的快速近似技术安而精心设计的。然而,它们并没有提供完全精确的结果,只要是超过精度能表示的范围就会产生误差。往往产生误差不是 因为数的大小,而是因为数的精度。因此,产生的结果接近但不等于想要的结果。尤其在使用 float  double 作精确运算的时候要特别小心。

将实数转换成浮点数

4.1  浮点数的规范化

同样的数值可以有多种浮点数表达方式,比如上面例子中的 123.45 可以表达为 12.345 × 101,0.12345 × 103 或者 1.2345 × 102。因为这种多样性,有必要对其加以规范化以达到统一表达的目标。规范的(Normalized)浮点数表达方式具有如下形式:

±d.dd...d × βe , (0 ≤ i < β)

其中 d.dd...d 即尾数,β 为基数,e 为指数。尾数中数字的个数称为精度,在本文中用 p 来表示。每个数字 d 介于 0 和基数之间,包括 0。小数点左侧的数字不为 0。

基于规范表达的浮点数对应的具体值可由下面的表达式计算而得:

±(d 0 + d 1β-1 + ... + p-1β-(p-1)e , (0 ≤ i < β)

对于十进制的浮点数,即基数 β 等于 10 的浮点数而言,上面的表达式非常容易理解,也很直白。计算机内部的数值表达是基于二进制的。从上面的表达式,我们可以知道,二进制数同样可以有小数点,也同样具有类似于十进制的表达方式。只是此时 β 等于 2,而每个数字 d 只能在 0 和 1 之间取值。比如二进制数 1001.101 相当于 1 × 2 3 + 0 × 22 + 0 × 21 + 1 × 20 + 1 × 2-1 + 0 × 2-2 + 1 × 2-3,对应于十进制的 9.625。其规范浮点数表达为 1.001101 × 23

4.2  根据精度表示浮点数

以上面的9.625为例,其规范浮点数表达为 1.001101 × 23

因此按单精度格式表示为:

1 10000010 00110100000000000000000

同理按双精度格式表示为:

1 10000000010 0011010000000000000000000000000000000000000000000000


可以考虑采用一些替代方案来实现。如通过 String 结合 BigDecimal 或 者通过使用 long 类型来转换。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章