為什麼不能使用float儲存金額

從計算機二進位制角度計算 6。6 + 1。3 的過程

不能使用float那用什麼型別儲存金額?

使用decimal儲存型別的缺點

《Java 2019 超神之路》

《Dubbo 實現原理與原始碼解析 —— 精品合集》

《Spring 實現原理與原始碼解析 —— 精品合集》

《MyBatis 實現原理與原始碼解析 —— 精品合集》

《Spring MVC 實現原理與原始碼解析 —— 精品合集》

《Spring Boot 實現原理與原始碼解析 —— 精品合集》

《資料庫實體設計合集》

《Java 面試題 —— 精品合集》

《Java 學習指南 —— 精品合集》

公司最近在做交易系統,交易系統肯定是要和錢打交道的,和錢有關,自然而然很容易想到用float儲存,但是使用float儲存金額做的計算是近似計算。老闆,用float做計算造成公司損失的錢都往你工資里扣

用 float 儲存金額,老闆直接說損失從工資里扣!

哼,扣工資就扣工資。但還是得靜下心來想想為什麼不能用float

為什麼不能使用float儲存金額

首先看個例子:FloatTest。java

public class FloatTest {

public static void main(String[] args) {

float f1 = 6。6f;

float f2 = 1。3f;

System。out。println(f1 + f2);

}

}

結果:7。8999996 和自己口算的值竟然不一樣

用 float 儲存金額,老闆直接說損失從工資里扣!

計算機只認識0和1,所有型別的計算首先會轉化為二進位制的計算

從計算機二進位制角度計算 6.6 + 1.3 的過程

float底層儲存

計算是由CPU來完成的,CPU表示浮點數由三部分組成 分為三個部分,符號位(sign),指數部分(exponent)和有效部分(fraction, mantissa)。其中float總共佔用32位,符號位,指數部分,有效部分各佔1位,8位,23位

用 float 儲存金額,老闆直接說損失從工資里扣!

二進位制的轉化

對於實數,轉化為二進位制分為兩部分,第一部分整數部分,第二部分是小數部分。整數部分計算二進位制大家都很熟悉。

整數部分的計算:6轉化為二進位制

除以2結果小數部分630311101

所以6最終的二進位制為110

小數部分的計算

將小數乘以2,取整數部分作為二進位制的值,然後再將小數乘以2,再取整數部分,以此往復迴圈

0.6轉化為二進位制

乘以2整數部分小數部分1。210。20。400。40。800。81。610。61。210。2

。。。進入迴圈,迴圈體為1001 所以0。6轉化為二進位制為0。10011001。。。 6。6轉化為二進位制為110。10011001。。。

規約化

透過規約化將小數轉為規約形式,類似科學計數法,就是保證小數點前面有一個有效數字。在二進位制裡面,就是保證整數位是一個1。110。10011001規約化為:1。1010011001*2^2

指數偏移值

指數偏移值 = 固定值 + 規約化的指數值 固定值=2^(e-1)-1,其中的e為儲存指數部分的位元位數,前面提到的float為8位。所以float中規定化值為127 6。6的二進位制值規約化以後為1。1010011001*2^2,指數是2,所以偏移值就是127+2=129,轉換為二進位制就是10000001,

拼接6.6

6。6為正數,符號位為0,指數部分為偏移值的二進位制10000001,有效部分為規約形式的小數部分,取小數的前23位即10100110011001100110011,最後拼接到一起即 01000000110100110011001100110011 到這裡已經大致可以知道float為什麼不精確了,首先在儲存的時候就會造成精度損失了,在這裡小數部分的二進位制是迴圈的,但是仍然只能取前23位。double造成精度損失的原因也是如此

求和

原來如此

用 float 儲存金額,老闆直接說損失從工資里扣!

不能使用float那用什麼型別儲存金額?

使用int 資料庫儲存的是金額的分值,顯示的時候在轉化為元

使用decimal mysql中decimal儲存型別的使用

column_name decimal(P,D);

D:代表小數點後的位數 P:有效數字數的精度,小數點也算一位 測試例子 資料表的建立:

CREATE TABLE `test_decimal` (

`id` int(11) NOT NULL,

`amount` decimal(10,2) NOT NULL

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

對應的DAO層程式碼:TestDecimalDao。java

/**

* @description dao層

*

* @author JoyHe

* @date 2018/11/05

* @version 1。0

*/

@Repository

public interface TestDecimalDao {

@Select(“select * from test_decimal where id = #{id}”)

TestDecimal getTestDecimal(int id);

}

測試類:TestDecimalDaoTest。java

/**

* @description 測試類

*

* @author JoyHe

* @date 2018/11/05

* @version 1。0

*/

public class TestDecimalDaoTest extends BaseTest {

@Resource

private TestDecimalDao testDecimalDao;

@Test

public void test() {

TestDecimal testDecimal1 = testDecimalDao。getTestDecimal(1);

TestDecimal testDecimal2 = testDecimalDao。getTestDecimal(2);

BigDecimal result = testDecimal1。getAmount()。add(testDecimal2。getAmount());

System。out。println(result。floatValue());

}

}

說明:jdbcType為decimal轉化為javaType為BigDecimal 測試結果:

用 float 儲存金額,老闆直接說損失從工資里扣!

是符合預期的7。9

使用decimal儲存型別的缺點

佔用儲存空間。浮點型別在儲存同樣範圍的值時,通常比decimal使用更少的空間

使用decimal計算效率不高

因為使用decimal時間和空間開銷較大,選用int作為資料庫儲存格式比較合適,可以同時避免浮點儲存計算的不精確和decimal的缺點。對於儲存數值較大或者保留小數較多的數字,資料庫儲存結構可以選擇bigint,可以同時避免浮點儲存計算不精準和DECIMAL精度計算代價高的問題

http://

juejin。im/post/5c08db5f

f265da611e4d7417

《Java 2019 超神之路》

《Dubbo 實現原理與原始碼解析 —— 精品合集》

《Spring 實現原理與原始碼解析 —— 精品合集》

《MyBatis 實現原理與原始碼解析 —— 精品合集》

《Spring MVC 實現原理與原始碼解析 —— 精品合集》

《Spring Boot 實現原理與原始碼解析 —— 精品合集》

《資料庫實體設計合集》

《Java 面試題 —— 精品合集》

《Java 學習指南 —— 精品合集》