前言
我們都知道,java中對大小數(shù),高精度的計算都會用到bigdecimal.但是在實際應(yīng)用中,運用bigdecimal還是會遇到一些問題,下面話不多說了,來一起看看詳細的介紹吧
問題描述:
程序中需要判斷一個字段是否為0(字段類型為bigdecimal),想都沒想,對象的判斷用equals?結(jié)果卻與預(yù)期有一定的差距,看下面代碼及運行結(jié)果。
1
2
3
4
5
|
public static void main(string[] args) { bigdecimal decimal1 = bigdecimal.valueof( 0 ); bigdecimal decimal2 = new bigdecimal( "0.00" ); system.out.println( "the result is " +decimal1.equals(decimal2)); } |
運行結(jié)果:
the result is false
結(jié)論: bigdecimal類型比較相等不能簡單的通過equals方法實現(xiàn)。
bigdecimal類的equals方法源碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public boolean equals(object x) { if (!(x instanceof bigdecimal)) return false ; bigdecimal xdec = (bigdecimal) x; if (x == this ) return true ; if (scale != xdec.scale) //這里會比較數(shù)字的精度 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 .inflate().equals(xdec.inflate()); } |
看上面的注釋可以知道,bigdecimal類的equals方法會判斷數(shù)字的精度,看下面的代碼及運行結(jié)果:
1
2
3
4
5
|
public static void main(string[] args) { bigdecimal decimal1 = bigdecimal.valueof( 0 ).setscale( 2 ); bigdecimal decimal2 = new bigdecimal( "0.00" ).setscale( 2 ); system.out.println( "the result is " +decimal1.equals(decimal2)); } |
運行結(jié)果:
the result is true
結(jié)論: 使用bigdecimal類equals方法判斷兩個bigdecimal類型的數(shù)據(jù)時,需要設(shè)置精度,否則結(jié)果可能不正確。
思考:每次都設(shè)置精度比較麻煩,有其他方式進行相等的比較嗎?
看了下bigdecimal的方法列表,有一個名為compareto的方法,通過注釋可知,貌似可以進行不同精度的比較,看下面的代碼。
1
2
3
4
5
|
public static void main(string[] args) { bigdecimal decimal1 = bigdecimal.valueof( 1.1 ); bigdecimal decimal2 = new bigdecimal( "1.10" ); system.out.println( "the result is " +decimal1.compareto(decimal2)); } |
運行結(jié)果:
the result is 0
0表示兩個數(shù)相等,所有可以通過compareto實現(xiàn)不同精度的兩個bigdecimal類型的數(shù)字是否相等的比較
引出的問題:公司的項目中,為了避免由于精度丟失引起問題,凡是有精度要求的字段用的都是bigdecimal類型。數(shù)據(jù)持久層用的是mybatis框架,mybatis的mapper文件中有些條件判斷用的是bigdecimal對應(yīng)的字段,如下:
1
2
3
4
5
6
|
<select id= "selectbycondition" resulttype= "com.scove.demo.domain.score" > select * from tb_score where 1 = 1 < if test= "score!=null and score!=0" > and score>#{score} </ if > ... |
score是一個bigdecimal類型的字段,score!=0 mybatis是如何進行判斷的,會不會用的是上面的equals方法?如果是那么項目上線會不會捅大簍子,想到這兒,有點怕了。寫了個程序測了下,這樣寫完全沒問題,能夠達到我想要的目的。但是還是有點擔心,看看mybatis底層是如何實現(xiàn)的吧,以免以后犯類似的錯誤嘛。
經(jīng)過分析調(diào)試,很快就找到了關(guān)鍵代碼的位置,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class expressionevaluator { public boolean evaluateboolean(string expression, object parameterobject) { object value = ognlcache.getvalue(expression, parameterobject); if (value instanceof boolean ) { return ( boolean ) value; } if (value instanceof number) { return ! new bigdecimal(string.valueof(value)).equals(bigdecimal.zero); } return value != null ; } ... |
1
2
3
4
5
6
7
8
9
10
11
|
public final class ognlcache { .... public static object getvalue(string expression, object root) { try { map<object, ognlclassresolver> context = ognl.createdefaultcontext(root, new ognlclassresolver()); return ognl.getvalue(parseexpression(expression), context, root); } catch (ognlexception e) { throw new builderexception( "error evaluating expression '" + expression + "'. cause: " + e, e); } } |
用的是表達式求值,ognl這個類的竟然沒有源碼,apache的官網(wǎng)上找了下,是有相應(yīng)的源碼的,只是需要單獨下載,真麻煩,算了不看了。據(jù)說底層用的是spring 的ognl表達式,我也不 關(guān)心了。但是以后如果不確定mapper中的test是否正確咋個辦?
想了下,還是寫一個工具類在拿不準的時候用一下吧,反正拿不準的時候肯定很少,所以隨便寫了簡單的吧,如下:
1
2
3
4
5
6
7
8
|
public static void main(string[] args) { expressionevaluator evaluator = new expressionevaluator(); string expression = "score!=null and score!=0" ; dynamiccontext context = new dynamiccontext( new configuration(), null ); context.bind( "score" , bigdecimal.valueof( 0.1 )); boolean flag = evaluator.evaluateboolean(expression , context.getbindings()); system.out.println( "the result is " +flag); } |
運行結(jié)果:
the result is true
總結(jié)
開發(fā)過程中,一定要細心去處理細節(jié)上的東西,不然一不小心就跳坑里了,輕則系統(tǒng)造成數(shù)據(jù)的不一致,修復(fù)數(shù)據(jù);重則造成重大的損失....
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,如果有疑問大家可以留言交流,謝謝大家對服務(wù)器之家的支持。
原文鏈接:https://www.cnblogs.com/falco/p/9500833.html