咖啡汪日誌———數值計算,精度、舍入、溢位(極客時間)
一直想要去跑跑數值計算的坑
今天終於還是抽出了時間
一,開篇有益
不知道你有沒有用手機計數器計算過10%+10%
其實結果是:
驚不驚喜,意不意外
為什麼不是0.2呢?
因為國外的計數器使用的都是單步計演算法
a+b%
代表的是 a*(1+b%)
Java採用了IEEE745標準實現浮點數的表達和計算
0.1
二進位制為:0.0 0011 0011 0011無線迴圈
十進位制為:0.1 000 000 000 000 000 555 111 512 312 …5625
下面是實際的展示:
二、浮點數運算避坑
1.使用BigDecimal表示和計算浮點數,務必使用字串的構造方法來初始化BigDecimal
new BigDecimal(0.1).add(new BigDecimal(0.2));//錯誤的方式
new BigDecimal("0.1").add(new BigDecimal("0.2"));//正確的方式
2.浮點數的字串格式化也要通過BigDecimal進行(不可使用valueOf/toString)
//(1)直接刪 ,結果為 2.34
System.out.println(new BigDecimal("2.3457").setScale(2,BigDecimal.ROUND_DOWN));
//(2)直接進位 , 結果為 2.35
System.out.println(new BigDecimal("2.3457").setScale(2,BigDecimal.ROUND_UP));
//(3)四捨五入,為5向上取 , 結果為2.35
System.out.println(new BigDecimal("2.3457").setScale(2,BigDecimal.ROUND_HALF_UP));
//(4)五為分水嶺,為5向下取 , 結果為2.34
System.out.println(new BigDecimal("2.3457").setScale(2,BigDecimal.ROUND_HALF_DOWN));
//(5)接近正無窮大的舍入模式
System.out.println(new BigDecimal("2.335").setScale(1,BigDecimal.ROUND_CEILING));//結果2.4
System.out.println(new BigDecimal("-2.335").setScale(1,BigDecimal.ROUND_CEILING));//結果-2.3
//(6)接近負無窮大的舍入模式
System.out.println( new BigDecimal("2.33").setScale(1,BigDecimal.ROUND_FLOOR));//結果2.3
System.out.println( new BigDecimal("-2.33").setScale(1,BigDecimal.ROUND_FLOOR));//結果-2.4
//(7)向“最接近”的數字舍入,距離相等,相相鄰的偶數舍入
System.out.println( new BigDecimal("1.15").setScale(1,BigDecimal.ROUND_HALF_EVEN));//結果1.2 ,也稱“”銀行家舍入法”(美國多用))
System.out.println( new BigDecimal("1.25").setScale(1,BigDecimal.ROUND_HALF_EVEN));//結果1.2
//(8)斷言請求的操作具有精確的結果,因此不需要舍入
System.out.println(new BigDecimal("1.15").setScale(1,BigDecimal.ROUND_HALF_UP));//結果1.2
3.BigDecimal使用equals 與 compareTo 做判等的區別:
(1)BigDecimal 的 equals 方法中註釋說明:equals 比較的是value和scale
1.0的scale 是1,1的dcale 是0,所以使用equals的比較結果為false
原始碼為:
/**
* Compares this {@code BigDecimal} with the specified
* {@code Object} for equality. Unlike {@link
* #compareTo(BigDecimal) compareTo}, this method considers two
* {@code BigDecimal} objects equal only if they are equal in
* value and scale (thus 2.0 is not equal to 2.00 when compared by
* this method).
*
* @param x {@code Object} to which this {@code BigDecimal} is
* to be compared.
* @return {@code true} if and only if the specified {@code Object} is a
* {@code BigDecimal} whose value and scale are equal to this
* {@code BigDecimal}'s.
* @see #compareTo(java.math.BigDecimal)
* @see #hashCode
*/
@Override
public boolean equals(Object x) {
if (!(x instanceof BigDecimal))
return false;
BigDecimal xDec = (BigDecimal) x;
if (x == this)
return true;
if (scale != xDec.scale)
return false;
long s = this.intCompact;
long xs = xDec.intCompact;
if (s != INFLATED) {
if (xs == INFLATED)
xs = compactValFor(xDec.intVal);
return xs == s;
} else if (xs != INFLATED)
return xs == compactValFor(this.intVal);
return this.inflated().equals(xDec.inflated());
}
(2)只是希望比較BigDecimal的 value,可以使用compareTo方法
原始碼如下:
/**
* Compares this {@code BigDecimal} with the specified
* {@code BigDecimal}. Two {@code BigDecimal} objects that are
* equal in value but have a different scale (like 2.0 and 2.00)
* are considered equal by this method. This method is provided
* in preference to individual methods for each of the six boolean
* comparison operators ({@literal <}, ==,
* {@literal >}, {@literal >=}, !=, {@literal <=}). The
* suggested idiom for performing these comparisons is:
* {@code (x.compareTo(y)} <<i>op</i>> {@code 0)}, where
* <<i>op</i>> is one of the six comparison operators.
*
* @param val {@code BigDecimal} to which this {@code BigDecimal} is
* to be compared.
* @return -1, 0, or 1 as this {@code BigDecimal} is numerically
* less than, equal to, or greater than {@code val}.
*/
public int compareTo(BigDecimal val) {
// Quick path for equal scale and non-inflated case.
if (scale == val.scale) {
long xs = intCompact;
long ys = val.intCompact;
if (xs != INFLATED && ys != INFLATED)
return xs != ys ? ((xs > ys) ? 1 : -1) : 0;
}
int xsign = this.signum();
int ysign = val.signum();
if (xsign != ysign)
return (xsign > ysign) ? 1 : -1;
if (xsign == 0)
return 0;
int cmp = compareMagnitude(val);
return (xsign > 0) ? cmp : -cmp;
}
4.HashSet 與 HashMap 不可與BigDecimal一起使用
HashMap中使用鍵物件來計算hashcode值,HashSet中使用成員物件來計算hashcode值,HashSet中的物件重寫了equals()和hashCode()方法
因此,如果放進去的是1.0,取時用 1 來取,是取不到的,參考上面BigDecimal 的 equals() 方法
解決辦法:
(1)使用TreeSet 替換HashSet(TreeSet使用CompareTo方法)
(2)在BigDecimal存入HashSet 與HashMap 中前,用stripTrailingZeros()方法去掉尾部的零,確保value相同的BigDecimal,scale也是相同的。
三、如何避免數值溢位問題?
1.使用Math類的 addExact.subtractExact 等XXExact方法進行數值運算
這樣數值溢位時,會主動丟擲異常
2.使用大數類BigInteger
BigInteget 執行Long 最大值加一,不會有問題
但溢位的資料,使用BigIngeter 的longValueExact(),轉為long時會報錯 ArithmeticException.
相關文章
- 深入淺出計算機組成原理-徐文浩-極客時間計算機
- MySQL 數值型別溢位處理MySql型別
- Dynamics CRM使用計算欄位自動計算兩個時間欄位的天數差
- 程式設計師的數學基礎課-黃申-極客時間程式設計師
- SPSS計算極值、平均值、中位數、方差、偏度、峰度、變異係數SPSS
- js數值精度JS
- oracle數值精度Oracle
- 日誌和實時流計算處理
- 10g PLSQL數值型別的溢位SQL型別
- python 計算中位數、四分位數、最大值、最小值等Python
- 整數溢位
- [Q]怎麼樣快速計算事務的時間與日誌量 zt
- JavaScript數字上舍入和下舍入JavaScript
- 程式設計師進階攻略-胡峰-極客時間程式設計師
- 咖啡汪日誌——實際開發中如何避免快取穿透和快取雪崩(程式碼示例實際展示)快取穿透
- 計算歸檔日誌所需要的磁碟空間
- ORACLE動態效能檢視統計值溢位Oracle
- 利用字串實現高精度數值運算(四)字串
- 利用字串實現高精度數值運算(三)字串
- 利用字串實現高精度數值運算(二)字串
- 利用字串實現高精度數值運算(一)字串
- 玩轉Git三劍客-蘇玲-極客時間Git
- WPS表格數值舍入函式大閱兵函式
- 重學前端-程劭非-極客時間前端
- 軟體工程之美-寶玉-極客時間軟體工程
- 機器學習40講-王天一-極客時間機器學習
- 面試現場-白海飛-極客時間面試
- Octave 數值計算
- 如何計算兩個時間間隔的天數
- JavaScript 計算兩個時間相差天數JavaScript
- vue中使用decimal.js對前端數值型別進行高精度計算VueDecimalJS前端型別
- 成品直播原始碼,golang計算時間段內的工作日數量原始碼Golang
- Java併發程式設計實戰-王寶令-極客時間Java程式設計
- 設計模式之美-王爭-極客時間-返現24元設計模式
- 【數值計算方法】數值積分&微分
- 統計某個時間段的歸檔日誌大小
- 高精度計算合集
- 精度計算問題