一、8種基本數據類型(4整,2浮,1符,1布):
整型:byte(最小的數據類型)、short(短整型)、int(整型)、long(長整型);
浮點型:float(浮點型)、double(雙精度浮點型);
字符型:char(字符型);
布爾型:boolean(布爾型)。
二、整型中 byte、short、int、long 取值范圍
byte:一個字節有8位,去掉符號位還有7位,正數為避免進位還要減1,因此byte的取值范圍為:-2^7 ~ (2^7-1),也就是 -128~127 之間。
short:short用16位存儲,去掉符號位還有15位,正數為避免進位還要減1,因此short的取值范圍是:-2^15 ~ (2^15-1)。
int:整型用32位存儲,去掉符號位還有31位,正數為避免進位還要減1,因此整型的取值范圍是 -2^31 ~ (2^31-1)。
long:長整型用64位存儲,去掉符號位還有63位,正數為避免進位還要減1,因此長整型的取值范圍是 -2^63 ~ (2^63-1)。
三:浮點型數據
浮點類型是指用於表示小數的數據類型。
單精度和雙精度的區別:
單精度浮點型float,用32位存儲,1位為符號位, 指數8位, 尾數23位,即:float的精度是23位,能精確表達23位的數,超過就被截取。
雙精度浮點型double,用64位存儲,1位符號位,11位指數,52位尾數,即:double的精度是52位,能精確表達52位的數,超過就被截取。
雙精度類型double比單精度類型float具有更高的精度,和更大的表示范圍,常常用於科學計算等高精度場合。
浮點數與小數的區別:
1)在賦值或者存儲中浮點類型的精度有限,float是23位,double是52位。
2)在計算機實際處理和運算過程中,浮點數本質上是以二進制形式存在的。
3)二進制所能表示的兩個相鄰的浮點值之間存在一定的間隙,浮點值越大,這個間隙也會越大。如果此時對較大的浮點數進行操作時,浮點數的精度問題就會產生,甚至出現一些“不正常"的現象。
為什么不能用浮點數來表示金額
先給出結論:金額用BigDecimal !!!
1)精度丟失問題
從上面我們可以知道,float的精度是23位,double精度是63位。在存儲或運算過程中,當超出精度時,超出部分會被截掉,由此就會造成誤差。
對於金額而言,舍去不能表示的部分,損失也就產生了。
32位的浮點數由3部分組成:1比特的符號位,8比特的階碼(exponent,指數),23比特的尾數(Mantissa,尾數)。這個結構會表示成一個小數點左邊為1,以底數為2的科學計數法表示的二進制小數。浮點數的能表示的數據大小范圍由階碼決定,但是能夠表示的精度完全取決於尾數的長度。long的最大值是2的64次方減1,需要63個二進制位表示,即便是double,52位的尾數也無法完整的表示long的最大值。不能表示的部分也就只能被舍去了。對於金額,舍去不能表示的部分,損失也就產生了。
了解了浮點數表示機制后,丟失精度的現象也就不難理解了。但是,這只是浮點數不能表示金額的原因之一。還有一個深刻的原因與進制轉換有關。十進制的0.1在二進制下將是一個無線循環小數。
eg:
public class MyTest { public static void main(String[] args) { float increment = 0.1f; float expected = 1; float sum = 0; for (int i = 0; i < 10; i++) { sum += increment; System.out.println(sum); } if (expected == sum) { System.out.println("equal"); } else { System.out.println("not equal "); } } }
輸出結果:
0.1
0.2
0.3
0.4
0.5
0.6
0.70000005
0.8000001
0.9000001
1.0000001
not equal
2)進制轉換誤差
從上面我們可以知道,在計算機實際處理和運算過程中,浮點數本質上是以二進制形式存在的。
而十進制的0.1在二進制下將是一個無限循環小數,這就會導致誤差的出現。
如果一個小數不是2的負整數次冪,用浮點數表示必然產生浮點誤差。
換言之:A進制下的有限小數,轉換到B進制下極有可能是無限小數,誤差也由此產生。
金額計算不能用doube!!!! 金額計算不能用doube!!!! 金額計算不能用doube!!!! 金額計算必須用BigDecimal
浮點數不精確的根本原因在於:尾數部分的位數是固定的,一旦需要表示的數字的精度高於浮點數的精度,那么必然產生誤差!
解決這個問題的方法是BigDecimal的類,這個類可以表示任意精度的數字,其原理是:用字符串存儲數字,轉換為數組來模擬大數,實現兩個數組的數學運算並將結果返回。
BigDecimal的使用要點:
1、BigDecimal變量初始化——必須用傳入String的構造方法
BigDecimal num1 = new BigDecimal(0.005);//用數值轉換成大數,有誤差 BigDecimal num12 = new BigDecimal("0.005");//用字符串轉換成大數,無誤差
因為:不是所有的浮點數都能夠被精確的表示成一個double 類型值,有些浮點數值不能夠被精確的表示成 double 類型值時,它會被表示成與它最接近的 double 類型的值,此時用它來初始化一個大數,會“先造成了誤差,再用產生了誤差的值生成大數”,也就是“將錯就錯”。
2、使用除法函數在divide的時候要設置各種參數,要精確的小數位數和舍入模式,其中有8種舍入模式:
1、ROUND_UP 遠離零的舍入模式。 在丟棄非零部分之前始終增加數字(始終對非零舍棄部分前面的數字加1)。 注意,此舍入模式始終不會減少計算值的大小。 2、ROUND_DOWN 接近零的舍入模式。 在丟棄某部分之前始終不增加數字(從不對舍棄部分前面的數字加1,即截短)。 注意,此舍入模式始終不會增加計算值的大小。 3、ROUND_CEILING 接近正無窮大的舍入模式。 如果 BigDecimal 為正,則舍入行為與 ROUND_UP 相同; 如果為負,則舍入行為與 ROUND_DOWN 相同。 注意,此舍入模式始終不會減少計算值。 4、ROUND_FLOOR 接近負無窮大的舍入模式。 如果 BigDecimal 為正,則舍入行為與 ROUND_DOWN 相同; 如果為負,則舍入行為與 ROUND_UP 相同。 注意,此舍入模式始終不會增加計算值。 5、ROUND_HALF_UP 向“最接近的”數字舍入,如果與兩個相鄰數字的距離相等,則為向上舍入的舍入模式。 如果舍棄部分 >= 0.5,則舍入行為與 ROUND_UP 相同;否則舍入行為與 ROUND_DOWN 相同。 注意,這是我們大多數人在小學時就學過的舍入模式(四舍五入)。 6、ROUND_HALF_DOWN 向“最接近的”數字舍入,如果與兩個相鄰數字的距離相等,則為上舍入的舍入模式。 如果舍棄部分 > 0.5,則舍入行為與 ROUND_UP 相同;否則舍入行為與 ROUND_DOWN 相同(五舍六入)。 7、ROUND_HALF_EVEN 向“最接近的”數字舍入,如果與兩個相鄰數字的距離相等,則向相鄰的偶數舍入。 如果舍棄部分左邊的數字為奇數,則舍入行為與 ROUND_HALF_UP 相同; 如果為偶數,則舍入行為與 ROUND_HALF_DOWN 相同。 注意,在重復進行一系列計算時,此舍入模式可以將累加錯誤減到最小。 此舍入模式也稱為“銀行家舍入法”,主要在美國使用。 如果前一位為奇數,則入位,否則舍去。 以下例子為保留小數點1位,那么這種舍入方式下的結果。 1.15>1.2 1.25>1.2 8、ROUND_UNNECESSARY 斷言請求的操作具有精確的結果,因此不需要舍入。 如果對獲得精確結果的操作指定此舍入模式,則拋出ArithmeticException。