1. 代碼
2.Java中數字類型的轉換法則
test1中看似除數中的所有的因子都被約掉了,只剩下了1000。但實際的輸出卻是5,而不是我們期望的1000。究其原因,是因為MICROS_PER_DAY按int類型進行的計算,而計算的結果是86400000000,已經超出了int類型的最大值,即溢出了(因int為32位,2^31-1=2147483647),24*60*60*1000*1000最後的結果是500654080(見程式中的print1的輸出)。
在產生了錯誤的計算結果後,該結果被付給了long型的MICROS_PER_DAY,long型為64位,故保持了這個錯誤的結果,最終導致了最終結果的錯誤。
解決該問題的方法是,通過使用long常量來代替int常量作為每一個乘積的第一個因子,這樣就可以強製表達式中所有的後續計算都使用long運算來完成,這樣就不會丟失精度,即:
long MICROS_PER_DAY = 24L*60*60*1000*1000;
圖中的六個實箭頭表示了無資訊損失的轉換,而三個虛箭頭表示的轉換則可能丟失精度。
3.浮點類型float, double的資料不適合在不容許舍入誤差的金融計算領域。
例如上面的test2,我們預期的得到的結果是0.1,但實際的輸出卻是0.8999999999999999。
這種誤差產生的原因是因為浮點數實際上是用二進位系統資料表示的。而分數1/10在二進位系統中沒有精確的表示,其道理就如同在十進位系統中無法精確表示1/3一樣。看完下面的第4點就可以明白其中的原因了。
如果需要進行不產生舍入誤差的精確數字計算,需要使用BigDecimal類。
4.既然說到了浮點型是使用二進位表示的,那麼就再來複習以下這方面的內容。
1)簡單介紹下IEEE754標準
Java中的float,double以及其對應的封裝類Float和Double,都依據IEEE754標準。
一個實數V在IEEE 754標準中可以用V=(-1)s×M×2E的形式表示,說明如下:
(1)符號s (sign)決定實數是正數(s=0)還是負數(s=1),對數值0的符號位特殊處理。
(2)有效數字M是二進位小數,M的取值範圍在1≤M<2或0≤M<1。
說明:尾數M用原碼表示。
根據原碼的規格化方法,最高數字位(整數部分)總是1,該標準將這個1預設
儲存,使得尾數表示範圍比實際儲存的一位。即M儲存的只是小數部分。
(看到後面的例子就會明白啦)
(3)指數E是2的冪,它的作用是對浮點數加權。
說明:由於E是用移碼表示,32位的float類型需要加上位移量127,64位的double
類型要加上位移量1023
下圖即為float(32位)和double(64位)的儲存格式:
2)十進位小數與二進位小數的相互轉換
例1:二進位轉十進位
例2:十進位數轉換成位元,是把整數部分和小數部分分別轉換,整數部分用2除,取餘數,小數部分用2乘,取整數位。
如:把(13.125)10轉換成位元
1)整數部分:13/2 商6 餘1
6/2 商3 餘0
3/2 商1 餘1
1/2 商0 餘1
故整數部分為 1101
2)小數部分:
因此,
3) 下面再舉幾個正常化表示的例子(以float類型為例):
a) 十進位小數1.25
二進位表示為1.01
正常化二進位表示即*1.01*
符號位:0
指數部分:0+127=127
即01111111
尾數部分:01000000000000000000000
註:尾數部分只儲存了小數部分的(0.01),整數部分的1是預設儲存的。這正好
驗證了4-1)-(2)要說明的問題。
最終結果為 0 01111111 01000000000000000000000