问题描述
在项目中用Java做浮点数计算时,发现对于4.015*100这样的计算,结果不是预料中的401.5,而是401.49999999999994。如此长的位数,对于显示来说很不友好。
问题原因:浮点数表示
查阅相关资料,发现原因是:计算机中的浮点数并不能完全精确表示。例如,对于一个double型的38414.4来说,计算机是这样存储它的:
转成二进制:1001011000001110.0110011001100110011001100110011001100
转成科
学计数法:1.0010110000011100110011001100110011001100110011001100×2^15
double型编码格式是这样的:
double 符号位1位 阶码11位 尾数52位
符号位:正数统一是0
阶码:15是正数,因此最高位是1,最低位减1,为10000001110
尾数:去掉最高位默认的1,为0010110000011100110011001100110011001100110011001100
组合起来,最终得到的编码是:0 10000001110 0010110000011100110011001100110011001100110011001100
从这里可以看出来,主要原因在于二进制编码使得小数部分无法完全精确表示,例如0.4 = 0.25 + 0.125 + ...,只能无限接近。所以在对浮点数做计算时会产生精度误差。
解决办法:高精度
Java中的BigDecimal可以支持任意精度的浮点数运算。在《Effective Java 》这本书中建议:float 和double 用来做科学计算或者是工程计算,而在商业计算中使用java.math.BigDecimal 。
BigDecimal有多种构造方法,如BigDecimal(double),BigDecimal(String),需要注意的是:构造参数为String类型时才能保证不丢失精度,因为double类型本身就是不完全精确的。故需要写成这样:BigDecimal("0.02")。
double类型的基本运算都能在BigDecimal中找到相对应的方法。另外,BigDecimal还可以配合NumberFormat做格式化输出。
BigDecimal在做运算的时候都会生成新的BigDecimal对象,因此相对double来说会带来更多的性能开销。
高精度实现初探
那么BigDecimal是如何做到能够表示任意精度的呢?这里只做一个初步的分析。
首先看BigInteger的实现。普通的int型是32位,因此有范围限制。BigInteger中有成员变量int[] mag,这样变长的int数组使得表示任意大小的整数成为可能。
再看BigDecimal的实现。它的官方介绍中说,任意一个BigDecimal都可以表示为unscaledValue × 10^-scale的形式。unscaledValue是一个任意大小的整数,在源代码中对应BigInteger intVal这个成员变量;scale是阶数,在源代码中对应int scale这个变量。这样就在BigInteger的基础上得到了BigDecimal的实现。
以上所述是小编给大家介绍的Java中浮点数精度问题的解决方法,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言。
问题内容: 我发现了PHP中浮点问题的一些解决方法: php.ini设置 假设是php.ini设置 那有多糟? 1.如果我只需要精确的2位数字(货币),我可以依靠此解决方案吗? 2.如果解决方案失败了,您能否为我提供一个清晰的例子? 编辑:3.哪个php.ini.precision值最适合两位数,金钱计算 请注意,我不能使用整数计算(float * 100 =美分),这样做太迟了。 我不会处理高于
问题内容: 是否在任何地方都有Java库可以对IEEE 754半精度 数字执行计算或将其与双精度数字进行转换? 这些方法中的任何一种都是合适的: 将数字保持为半精度格式,并使用整数算术和位扭曲(如MicroFloat的单精度和双精度)进行计算 以单精度或双精度执行所有计算,转换成半精度以进行传输(在这种情况下,我需要经过良好测试的转换函数。) 编辑 :转换需要100%准确- 输入文件中 有 很多N
问题内容: 我该如何克服Java android中双精度乘法的精度问题?请注意,我正在将字符串值转换为double值。 例如:当我将两个double值相乘时: 我得到以下结果:0.8999999999999999 我得到的一些结果是。 0.6 * 3 = 1.7999999999999998; 0.2 * 0.2 = 0.04000000000000001; 等等 除了上述结果外,我想得到以下结果
问题内容: 如果我们运行以下代码: 它打印: 文字1.2345678990922222中的长尾将被忽略,但1.22222222222222222222中的长尾不会被忽略(变量d中的最后一个十进制数字变为3而不是2)。为什么? 问题答案: 打印a 或a 时看到的位数是Java的默认规则(从和转换为十进制)的结果。 Java的浮点数默认格式使用最少的有效十进制数字来将数字与附近的可表示数字区分开。1个
问题内容: $a = ‘35’; $b = ‘-34.99’; echo ($a + $b); 结果为0.009999999999998 这是怎么回事?我想知道为什么我的程序不断报告奇怪的结果。 为什么PHP不返回预期的0.01? 问题答案: 因为浮点运算!=实数运算。对于一些浮子和,由不精确性引起的差异的说明是。这适用于使用浮点数的任何语言。 由于浮点数是具有有限精度的二进制数,因此存在有限数量
本文向大家介绍Fortran 浮点数精度,包括了Fortran 浮点数精度的使用技巧和注意事项,需要的朋友参考一下 示例 类型的浮点数real不能有任何实数值。它们可以表示实数,最多可以包含一定数量的十进制数字。 FORTRAN 77保证了两种浮点类型,而最新的标准则至少保证了两种实数类型。实变量可以声明为 x这是默认类型的实数,并且y是比更大的十进制精度的实数x。在Fortran 2008中,十