聊聊Java Double相加出現(xiàn)的怪事
Java Double相加出現(xiàn)的怪事
問題的提出
編譯運行下面這個程序會看到什么
public class test {
public static void main(String args[]) {
System.out.println(0.05 + 0.01);
System.out.println(1.0 - 0.42);
System.out.println(4.015 * 100);
System.out.println(123.3 / 100);
}
};
你沒有看錯!結果確實是
0.060000000000000005
0.5800000000000001
401.49999999999994
1.2329999999999999
Java中的簡單浮點數(shù)類型float和double不能夠進行運算。不光是Java,在其它很多編程語言中也有這樣的問題。在大多數(shù)情況下,計算的結果是準確的,但是多試幾次(可以做一個循環(huán))就可以試出類似上面的錯誤?,F(xiàn)在終于理解為什么要有BCD碼了。
這個問題相當嚴重,如果你有9.999999999999元,你的計算機是不會認為你可以購買10元的商品的。
在有的編程語言中提供了專門的貨幣類型來處理這種情況,但是Java沒有?,F(xiàn)在讓我們看看如何解決這個問題。
解決方案
現(xiàn)在我們已經(jīng)可以解決這個問題了,原則是使用BigDecimal并且一定要用String來夠造。
但是想像一下吧,如果我們要做一個加法運算,需要先將兩個浮點數(shù)轉(zhuǎn)為String,然后夠造成BigDecimal,在其中一個上調(diào)用add方法,傳入另一個作為參數(shù),然后把運算的結果(BigDecimal)再轉(zhuǎn)換為浮點數(shù)。你能夠忍受這么煩瑣的過程嗎?下面我們提供一個工具類Arith來簡化操作。它提供以下靜態(tài)方法,包括加減乘除和四舍五入:
public static double add(double v1, double v2);
public static double sub(double v1, double v2);
public static double mul(double v1, double v2);
public static double div(double v1, double v2);
public static double div(double v1, double v2, int scale);
public static double round(double v, int scale);
package org.nutz.mvc.core;
import java.math.BigDecimal;
public class Arith {
// 源文件Arith.java:
/**
* 由于Java的簡單類型不能夠精確的對浮點數(shù)進行運算,這個工具類提供精 確的浮點數(shù)運算,包括加減乘除和四舍五入。
*/
// 默認除法運算精度
private static final int DEF_DIV_SCALE = 10;
// 這個類不能實例化
private Arith() {
}
/**
* 提供精確的加法運算。
*
* @param v1
* 被加數(shù)
* @param v2
* 加數(shù)
* @return 兩個參數(shù)的和
*/
public static double add(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2).doubleValue();
}
/**
* 提供精確的減法運算。
*
* @param v1
* 被減數(shù)
* @param v2
* 減數(shù)
* @return 兩個參數(shù)的差
*/
public static double sub(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2).doubleValue();
}
/**
* 提供精確的乘法運算。
*
* @param v1
* 被乘數(shù)
* @param v2
* 乘數(shù)
* @return 兩個參數(shù)的積
*/
public static double mul(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2).doubleValue();
}
/**
* 提供(相對)精確的除法運算,當發(fā)生除不盡的情況時,精確到 小數(shù)點以后10位,以后的數(shù)字四舍五入。
*
* @param v1
* 被除數(shù)
* @param v2
* 除數(shù)
* @return 兩個參數(shù)的商
*/
public static double div(double v1, double v2) {
return div(v1, v2, DEF_DIV_SCALE);
}
/**
* 提供(相對)精確的除法運算。當發(fā)生除不盡的情況時,由scale參數(shù)指 定精度,以后的數(shù)字四舍五入。
*
* @param v1
* 被除數(shù)
* @param v2
* 除數(shù)
* @param scale
* 表示表示需要精確到小數(shù)點以后幾位。
* @return 兩個參數(shù)的商
*/
public static double div(double v1, double v2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* 提供精確的小數(shù)位四舍五入處理。
*
* @param v
* 需要四舍五入的數(shù)字
* @param scale
* 小數(shù)點后保留幾位
* @return 四舍五入后的結果
*/
public static double round(double v, int scale) {
if (scale < 0) {
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(v));
BigDecimal one = new BigDecimal("1");
return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
};
Double相加時出現(xiàn)的多位問題
String test = "40.61 , 18588.73, 29925.07, 7986.06, 18639.19, 25914.32, 32907.74, 34165.89, 9724.7, 52777.92";
String[] arr = test.split(",");
double sum = 0;
for(int i=0;i<arr.length;i++){
sum += Double.parseDouble(arr[i]);
}
System.out.println(sum);
結果: 230670.22999999998
查了查 沒有深入的了解,大概是java 的double機制,關于精度的問題,若是不出現(xiàn)這種情況,保留小數(shù)點后兩位就可以了?;蛘呤褂肂igDecimal進行計算,另外《effective java》這本書里也提過,double和float 不建議使用商業(yè)計算。
補充:原因在于我們的計算機是二進制的。浮點數(shù)沒有辦法是用二進制進行精確表示。
我們的CPU表示浮點數(shù)由兩個部分組成:指數(shù)和尾數(shù),這樣的表示方法一般都會失去一定的精確度,有些浮點數(shù)運算也會產(chǎn)生一定的誤差。
如:2.4的二進制表示并非就是精確的2.4。
反而最為接近的二進制表示是 2.3999999999999999。浮點數(shù)的值實際上是由一個特定的數(shù)學公式計算得到的。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
并發(fā)編程之Java內(nèi)存模型鎖的內(nèi)存語義
這篇文章主要介紹了并發(fā)編程之Java內(nèi)存模型鎖的內(nèi)存語義,鎖的作用是讓臨界區(qū)互斥執(zhí)行,本文只要圍繞鎖的內(nèi)存語義展開全文內(nèi)容,需要的小伙伴可以參考一下2021-11-11
在win10系統(tǒng)下,如何配置Spring Cloud alibaba Seata以及出現(xiàn)問題時怎么解決
今天教大家如何在win10系統(tǒng)下,配置Spring Cloud alibaba Seata以及出現(xiàn)問題時怎么解決,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下2021-06-06
詳解OpenAPI開發(fā)如何動態(tài)的添加接口實現(xiàn)
這篇文章主要為大家介紹了OpenAPI開發(fā)如何動態(tài)的添加接口實現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-04-04

