原因: 二进制不能准确的表示十进制的浮点数. 由十进制转换二进制的方法可以知道: 如果最后一位不是0或5,长度是无限的.
解决: 转为bigdecimal进行运算.
再详尽一些的原因和示例: EffectiveJava中是这样写的.
第60条: 如果需要精确的答案,请避免使用float和double:
float double 类型主要是为了科学计算和工程计算而设计的 它们执行二进制浮点
运算( binary floating-point arithmetic ),这是为了在广泛的数值范围上提供较为精确的快速
近似计算而精心设计的 然而,它们并没有提供完全精确的结果,所以不应该被用于需要精
确结果的场合 float double 类型尤其不适合用于货币计算 ,因为要让一个 float
double 精确地表示 0.1 (或者 的任何其他负数次方值 )是不可能的
例如,假设你的口袋中有 $1.03 ,花掉了 42 之后还剩下多少钱呢?下面这个很简单的
程序片段试图回答这个问题:
System out.println( l.03 - 0 . 42 ) ;
遗憾的是,它输出的结果是 0. 6100000000000001 这并不是个别的例子 假设你的口
袋里有 $1 ,你买了 个垫圈,每个为 10 ¢ 那么应该找回多少零头呢?
System out.println(l.0- 9 * 0.10);
根据上述程序片段,你得到的是 $0.09999999999999998
你可能会认为,只要在打印之前将结果做 下舍人就可以解决这个问题,但遗憾的是,
这种做法并不总是可行的 例如,假设你的口袋里有 $1 ,你看到货架上有 美昧 的糖果,
标价分别为 10¢ 20¢ 30¢ ,等等,一直到 $1 你打算从标价为 10¢ 的糖果开始,每种买1
颗,一直到不能支付货架上下一种价格的糖果为止,那么你可以买多少颗糖果?还会找回多
少零头?下面是 个简单的程序,用来解决这个问题,示例略。
如果真正运行这个程序,你会发现可以支付 颗糖果,并且还剩下 $0.3999999999999999
这个答案是不正确的!解决这个问题的正确方法是使用 BigDecirnal int 或者 long 进行
货币计算
下面的程序是上一个程序的简单翻版,它使用 BigDecimal 类型代替 double
意,它使用了 BigDecimal String 构造器,而不是用 double 构造器 为了避免将不
正确的值引入到计算中,这是必需的: new BigDecimal (”1 . 00"),具体示例略
如果运行这个修改过的程序,会发现可以支付 颗糖果,还剩下 $0.00 这才是正确的
答案
然而,使用 BigDecimal 有两个缺点:与使用基本运算类型相比,这样做很不方便,
而且速度很慢 对于解决这样 个简单的问题,后 种缺点并不要紧 但是前 种缺点可能
会让你很不舒服
除了使用 BigDecimal 之外,还有一种办法是使用 int 或者 long ,到底选用 int
long 要取决于所涉及数值的大小,同时要自己处理十进制小数点,示例略。
总而言之,对于任何需要精确答案的计算任务,请不要使用 float 或者 double 如果
你想让系统来处理十进制小数点,并且不介意因为不使用基本类型而带来的不便,就请使用
BigDec mal 使用 BigDecimal 还有 些额外的好处,它允许你完全控制舍人,每当一
个操作涉及舍入的时候,你都可以从 种舍入模式中选择其- 如果你正通过合法强制的舍
入行为进行商务计算,使用 BigDecimal 是非常方便的 如果性能非常关键,并且你又不
介意自己处理十进制小数点,而且所涉及的数值又不太大,就可以使用 int 或者 long
果数值范围没有超过 位十进制数字,就可以使用工nt ;如果不超过 18 位数字,就可以使
long 如果数值可能超过 18 位数字,就必须使用 BigDecimal