首先來預熱一下
System.out.println(2.00 - 1.10);
System.out.println(2.00d - 1.10d);
System.out.println(2.00D - 1.10D);
答案
// 0.8999999999999999
// 0.8999999999999999
// 0.8999999999999999
PS:JAVA Puzzlers(Puzzle 2)
那么,問題是:
一個浮點數X,存到DB里是305.35,頁面顯示305.34
X = ?
DB用的是MySQL,字段類型是FLOAT(8,2)
后臺框架用的是Hibernate
前臺顯示用的是JSTL tag
答案
X = 305.345
原因
首先把305.345用Hibernate存入DB,DB的保存值會四舍五入為305.35(附錄1),而這時POJO里的值仍為305.345
而Java的Format默認規則是"4舍6入5奇偶"(附錄2、附錄3),所以顯示為了305.34
解決辦法
1、可以持久化后再從DB取一次值,然后再顯示,可保證DB和頁面的值保持一致
2、可以多設一位小數保留位,設為3。然后只用前2位
3(推薦)、不要用Float或Double,而用Decimal(Java、MySQL皆如此)
PS:
那這個問題又和預熱的題目有什么關系呢?
浮點數的數值是不確定的,遇到了這個問題后,我首先就想到了".99999"這種情況。
附錄是我親自測試的樣例,在測試過程中發現了不同版本MySQL在存儲浮點數上處理不一致(附錄1)。除去".99999"這種情況(限于本人能力有限就不深入研究下去了),可以確定MySQL用的是四舍五入,Java用的是4舍6入5奇偶。
附錄1 - 不同版本MySQL在存儲浮點數上的區別
MySQL 5.0.27
FLOAT(8,2)
305.345 -> 305.35
1305.345 -> 1305.35
305.34499999999997 -> 305.35
1305.34499999999997 -> 1305.35
-----------------------------------
MySQL 5.1.32
FLOAT(8,2)
305.345 -> 305.35
1305.345 -> 1305.35
305.34499999999997 -> 305.34
1305.34499999999997 -> 1305.35
-----------------------------------
MySQL 5.1.37
FLOAT(8,2)
305.345 -> 305.35
1305.345 -> 1305.35
305.34499999999997 -> 305.34
1305.34499999999997 -> 1305.35
-----------------------------------
PS:
使用浮點數可能會遇到意想不到的問題,因為在MySQL中的所有計算用雙精度完成。
http://dev.mysql.com/doc/refman/5.1/zh/problems.html#problems-with-float
附錄2 - JSTL tag的Format
<!-- 56 -->
<fmt:formatNumber value="${56.5}" pattern="#,###,###,###"/><br>
<!-- 305.34 -->
<fmt:formatNumber value="${305.344}" pattern="###,##0.00"/><br>
<!-- 305.35 -->
<fmt:formatNumber value="${305.354}" pattern="###,##0.00"/><br>
<!-- 1,305.34 -->
<fmt:formatNumber value="${1305.344}" pattern="###,##0.00"/><br>
<!-- 1,305.35 -->
<fmt:formatNumber value="${1305.354}" pattern="###,##0.00"/><br>
<!-- 305.34 -->
<fmt:formatNumber value="${305.345}" pattern="###,##0.00"/><br>
<!-- 1,305.34 -->
<fmt:formatNumber value="${1305.345}" pattern="###,##0.00"/><br>
<!-- 305.36 -->
<fmt:formatNumber value="${305.355}" pattern="###,##0.00"/><br>
<!-- 305.36 -->
<fmt:formatNumber value="${305.365}" pattern="###,##0.00"/><br>
<!-- 305.34 -->
<fmt:formatNumber value="${305.34499999999997}" pattern="###,##0.00"/><br>
<!-- 305.35 -->
<fmt:formatNumber value="${305.35499999999997}" pattern="###,##0.00"/><br>
<!-- 1,305.34 -->
<fmt:formatNumber value="${1305.34499999999997}" pattern="###,##0.00"/><br>
<!-- 1,305.36 -->
<fmt:formatNumber value="${1305.35499999999997}" pattern="###,##0.00"/><br>
附錄3 - Java的Format
@Test
public void formatTest() {
System.out.println(showFloat(305.344)); // 305.34
System.out.println(showFloat(305.354)); // 305.35
System.out.println(showFloat(1305.344)); // 1,305.34
System.out.println(showFloat(1305.354)); // 1,305.35
System.out.println(showFloat(305.345)); // 305.34
System.out.println(showFloat(1305.345)); // 1,305.34
System.out.println(showFloat(305.355)); // 305.36
System.out.println(showFloat(305.365)); // 305.36
BigDecimal decimal = new BigDecimal("305.345");
System.out.println(decimal.setScale(2, BigDecimal.ROUND_HALF_UP)); // 305.35(四舍五入)
System.out.println(showFloat(305.34499999999997)); // 305.34
System.out.println(showFloat(305.35499999999997)); // 305.35
System.out.println(showFloat(1305.34499999999997)); // 1,305.34
System.out.println(showFloat(1305.35499999999997)); // 1,305.36
}
private String showFloat(double d) {
return new DecimalFormat("###,###,###,##0.00").format(d);
}