成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

程序員是如何神不知鬼不覺的弄丟銀行1分錢的?

開發(fā) 開發(fā)工具
前段時(shí)間和某銀行合作共同開發(fā)了適合我們的一套支付系統(tǒng)。最近,我們對賬發(fā)現(xiàn)某些訂單始終都對不齊。銀行的下單金額與對賬金額始終少了1分錢。

前段時(shí)間和某銀行合作共同開發(fā)了適合我們的一套支付系統(tǒng)。最近,我們對賬發(fā)現(xiàn)某些訂單始終都對不齊。銀行的下單金額與對賬金額始終少了1分錢。

這就奇怪了,如果這種異常訂單一多就是少了很多錢。在涉及錢的金融領(lǐng)域,這是個(gè)很謹(jǐn)慎嚴(yán)肅的問題。

我們跟銀行排查發(fā)現(xiàn)了問題的原因,也就是我今天想聊的關(guān)于技術(shù)上的東西:double精度的丟失問題。

1問題復(fù)現(xiàn)

我們先舉個(gè)簡單的例子

  1. double result = 1.0 - 0.9; 

這段代碼中result等于多少?0.1么?如果執(zhí)行代碼的話,分分鐘打臉。

double精度丟失問題

2背后原理

無論是我們本文提到的double,還是float,都是浮點(diǎn)數(shù)。

在計(jì)算機(jī)科學(xué)中,浮點(diǎn)(英語:floating point,縮寫為FP)是一種對于實(shí)數(shù)的近似值數(shù)值表現(xiàn)法,由一個(gè)有效數(shù)字(即尾數(shù))加上冪數(shù)來表示,通常是乘以某個(gè)基數(shù)的整數(shù)次指數(shù)得到。以這種表示法表示的數(shù)值,稱為浮點(diǎn)數(shù)(floating-point number)。

計(jì)算機(jī)使用浮點(diǎn)數(shù)運(yùn)算的主因,在于計(jì)算機(jī)使用二進(jìn)位制的運(yùn)算。

例如:4÷2=2,4=100(二進(jìn)制)、2=010(二進(jìn)制)。在二進(jìn)制中除以2相當(dāng)于退一位數(shù)。

那么1.0÷2=0.5=0.1(二進(jìn)制)也就是1/2,依此類推二進(jìn)制的0.01(二進(jìn)制)就是十進(jìn)制 1/(2^2) = 1/4 = 0.25。

上面看到的1、0.5、0.25那都是可以轉(zhuǎn)換成二進(jìn)制的小數(shù),如十進(jìn)制的0.1,就無法用二進(jìn)制準(zhǔn)確的表示出來。因此只能使用近似值的方式表達(dá)。

比如,我們嘗試著把10進(jìn)制的0.1轉(zhuǎn)化成二進(jìn)制試試,步驟如下:

  1. 0.1*2=0.2……0——整數(shù)部分為“0”。整數(shù)部分“0”清零后為“0”,用“0.2”接著計(jì)算。 
  2. 0.2*2=0.4……0——整數(shù)部分為“0”。整數(shù)部分“0”清零后為“0”,用“0.4”接著計(jì)算。 
  3. 0.4*2=0.8……0——整數(shù)部分為“0”。整數(shù)部分“0”清零后為“0”,用“0.8”接著計(jì)算。 
  4. 0.8*2=1.6……1——整數(shù)部分為“1”。整數(shù)部分“1”清零后為“0”,用“0.6”接著計(jì)算。 
  5. 0.6*2=1.2……1——整數(shù)部分為“1”。整數(shù)部分“1”清零后為“0”,用“0.2”接著計(jì)算。 
  6. 0.2*2=0.4……0——整數(shù)部分為“0”。整數(shù)部分“0”清零后為“0”,用“0.4”接著計(jì)算。 
  7. 0.4*2=0.8……0——整數(shù)部分為“0”。整數(shù)部分“0”清零后為“0”,用“0.8”接著計(jì)算。 
  8. 0.8*2=1.6……1——整數(shù)部分為“1”。整數(shù)部分“1”清零后為“0”,用“0.6”接著計(jì)算。 
  9. 0.6*2=1.2……1——整數(shù)部分為“1”。整數(shù)部分“1”清零后為“0”,用“0.2”接著計(jì)算。 
  10. 0.2*2=0.4……0——整數(shù)部分為“0”。整數(shù)部分“0”清零后為“0”,用“0.4”接著計(jì)算。 
  11. 0.4*2=0.8……0——整數(shù)部分為“0”。整數(shù)部分“0”清零后為“0”,用“0.2”接著計(jì)算。 
  12. 0.8*2=1.6……1——整數(shù)部分為“1”。整數(shù)部分“1”清零后為“0”,用“0.2”接著計(jì)算。 
  13. …… 

可以發(fā)現(xiàn),這個(gè)過程是除不盡的,除出了一個(gè)***循環(huán)小數(shù):二進(jìn)制的 0.0001100110011…

那么,如何在計(jì)算機(jī)中表示這個(gè)***不循環(huán)的小數(shù)呢?只能考慮按照不同的精度保理不同的位數(shù)。

我們知道float是單精度的,double是雙精度的。不同的精度,其實(shí)就是保留的有效數(shù)字位數(shù)不同,保留的位數(shù)越多,精度越高。

所以,浮點(diǎn)數(shù)在Java中是無法精確表示的,因?yàn)榇蟛糠指↑c(diǎn)數(shù)轉(zhuǎn)換成二進(jìn)制是一個(gè)***不循環(huán)的小數(shù),只能通過保留精度的方式進(jìn)行近似表示。

在《阿里巴巴Java開發(fā)手冊》中其實(shí)也有著明確的規(guī)定,說明了小數(shù)類型禁止使用float或者double來表示。(雖然這條是Mysql相關(guān)規(guī)則,但是Java代碼同樣適用。)

3問題引申

我們現(xiàn)在基本已經(jīng)知道了double的精度問題是什么問題。

在實(shí)際的訂單交易過程中,出現(xiàn)這個(gè)問題的更多場景是金額單位元與分的轉(zhuǎn)換。銀行給你的單位是元,你自己的運(yùn)算是分;前端輸入是元,計(jì)算是分等等。

舉個(gè)例子:用戶下了一筆64.6元的訂單,你在需要轉(zhuǎn)換成分。如果直接除以100,你會(huì)發(fā)現(xiàn)計(jì)算出來的分始終是6459,少1分錢。

金額丟失1分問題

4解決方式

1)使用BigDecimal

為了解決這種浮點(diǎn)小數(shù)進(jìn)度丟失問題,java提供了一種計(jì)算方式BigDecimal。

BigDecimal

這樣就可以了么? 不是,這樣能解決大部分問題,假如其他系統(tǒng)或語言不支持BigDecimal呢。當(dāng)我們無法解決這個(gè)問題的時(shí)候,我們需要做的是想辦法規(guī)避這個(gè)問題帶來的影響。

2)以分為單位,Long為數(shù)據(jù)結(jié)構(gòu)存儲

目前我們某些核心系統(tǒng)在金額傳輸?shù)倪^程和存儲中還是以元存儲浮點(diǎn)數(shù)。導(dǎo)致低于10元的訂單計(jì)算利息費(fèi)率的時(shí)候,無法計(jì)算清楚,使得我們的業(yè)務(wù)服務(wù)在處理這些問題頭疼死了。

整數(shù)與整數(shù)的計(jì)算,就沒有這些精度丟失問題。Long取值范圍(9223372036854775807)完全夠用。

3)除不盡怎么辦

對于除法,始終會(huì)產(chǎn)生除不盡的情況怎么辦?有個(gè)詞叫軋差

什么意思呢?舉個(gè)簡單例子。假如現(xiàn)在需要把10元分成3分,如果是10除以3這么除,會(huì)發(fā)現(xiàn)為3.33333無窮盡的3。這些數(shù)字完全無法在程序或數(shù)據(jù)庫中進(jìn)行精確的存儲。

簡單理解就是,當(dāng)除不盡或需去除小數(shù)點(diǎn)的時(shí)候,前面的n-1筆(這里n=3)做四舍五入。***一筆做兜底(總金額減去前面n-1筆之和)。這樣保證總金額的不會(huì)丟失。

這里我們的具體應(yīng)用場景是用戶使用了現(xiàn)金券,然后有部分退款,計(jì)算應(yīng)退本金的問題。現(xiàn)金券的處理又是一大篇文章,這里以后有機(jī)會(huì)再介紹。

5總結(jié)能用Long不用浮點(diǎn)數(shù)存儲。

前后端傳輸金額(元)的時(shí)候,請使用字符串,不要使用浮點(diǎn)數(shù)。

浮點(diǎn)數(shù)運(yùn)算請使用BigDecimal。

實(shí)在無法除盡,可以考慮通過軋差的方式解決。

double精度不是坑,是個(gè)容易忽視的巨坑,小小經(jīng)驗(yàn)希望對大家有所幫助,謹(jǐn)慎對待。>-<

關(guān)于銀行1分差的問題,等待銀行修復(fù),歷史訂單做調(diào)賬處理,哈哈哈哈哈。

責(zé)任編輯:武曉燕 來源: 51CTO專欄
相關(guān)推薦

2011-07-07 10:34:31

2025-04-15 08:10:00

C 語言static代碼

2024-11-27 09:10:24

2020-07-21 07:00:00

側(cè)信道攻擊黑客網(wǎng)絡(luò)攻擊

2022-01-07 18:33:56

加密貨幣惡意軟件攻擊

2020-11-23 06:50:31

微信原視頻移動(dòng)應(yīng)用

2014-11-24 09:13:38

2024-01-15 14:06:00

2009-10-30 10:47:26

木馬電腦系統(tǒng)端口

2011-07-25 17:16:05

2015-03-04 13:26:59

加密解密后門

2016-12-26 13:20:33

大數(shù)據(jù)真實(shí)面目動(dòng)向

2018-02-09 10:40:52

2023-11-06 07:19:11

程序員GitHub社交媒體

2015-02-03 02:40:33

程序員盲人程序員

2013-11-04 09:39:16

程序員信仰

2010-03-02 10:13:56

程序員面試

2020-12-07 11:29:24

ReactVueVue3

2012-07-20 11:16:26

程序員

2015-06-16 13:00:43

程序員跳槽原因
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 99精品欧美 | www国产亚洲精品 | 97av视频| 天天爱综合 | 免费视频久久 | 中文字幕二区三区 | 久草网站| 欧美日韩高清一区二区三区 | 久久综合影院 | 91亚洲精品国偷拍自产在线观看 | 欧美视频第三页 | 韩国主播午夜大尺度福利 | 另类 综合 日韩 欧美 亚洲 | 精品久久精品 | 欧美久久久久久久 | 涩涩视频在线播放 | 久久国产一区二区 | www.国产精品 | 亚洲精品中文字幕 | 国产视频1区2区 | 国产欧美日韩一区二区三区 | 欧美一级电影免费 | 久久噜噜噜精品国产亚洲综合 | 不卡一区 | 婷婷激情综合 | 久久久久久国 | 欧美日韩综合精品 | 亚洲精品一区二区另类图片 | 亚洲精品天堂 | 日本三级网址 | 有码在线 | 欧美国产视频 | 久久国产精品网站 | 国产精品一区一区 | 久久久久国色av免费观看性色 | 在线视频一区二区 | 浴室洗澡偷拍一区二区 | 国产精品一区二区不卡 | 日韩欧美中文 | 国产精品成人一区二区三区夜夜夜 | 夜夜精品视频 |