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

解讀:大整數(shù)傳輸為何禁用Long類型?

開(kāi)發(fā) 開(kāi)發(fā)工具
最新發(fā)布的《Java開(kāi)發(fā)手冊(cè)(嵩山版)》增加了前后端規(guī)約,其中有一條:禁止服務(wù)端在超大整數(shù)下使用Long類型作為返回。這是為何?在實(shí)際開(kāi)發(fā)中可能出現(xiàn)什么問(wèn)題?本文從IEEE754浮點(diǎn)數(shù)標(biāo)準(zhǔn)講起,詳細(xì)解析背后的原理,幫助大家徹底理解這個(gè)問(wèn)題,提前避坑。

[[337333]]

最新發(fā)布的《Java開(kāi)發(fā)手冊(cè)(嵩山版)》增加了前后端規(guī)約,其中有一條:禁止服務(wù)端在超大整數(shù)下使用Long類型作為返回。這是為何?在實(shí)際開(kāi)發(fā)中可能出現(xiàn)什么問(wèn)題?本文從IEEE754浮點(diǎn)數(shù)標(biāo)準(zhǔn)講起,詳細(xì)解析背后的原理,幫助大家徹底理解這個(gè)問(wèn)題,提前避坑。

8月3日,這個(gè)在我等碼農(nóng)心中具有一定紀(jì)念意義的日子里,《Java開(kāi)發(fā)手冊(cè)》發(fā)布了嵩山版。每次發(fā)布我都特別期待,因?yàn)榭偰苷业揭恍┏绦騿T不得不重視的“血淋淋的巨坑”。比如這次,嵩山版中新增的模塊——前后端規(guī)約,其中一條禁止服務(wù)端在超大整數(shù)下使用Long類型作為返回。

 

這個(gè)問(wèn)題,我在實(shí)際開(kāi)發(fā)中遇到過(guò),所以印象也特別深。如果在業(yè)務(wù)初期沒(méi)有評(píng)估到這一點(diǎn),將訂單ID這類關(guān)鍵信息,按照Long類型返回給前端,可能會(huì)在業(yè)務(wù)中后期高速發(fā)展階段,突然暴雷,導(dǎo)致嚴(yán)重的業(yè)務(wù)故障。期望大家能夠重視。

這條規(guī)約給出了直接明確的避坑指導(dǎo),但要充分理解背后的原理,知其所以然,還有很多點(diǎn)要思考。首先,我們來(lái)看幾個(gè)問(wèn)題,如果能說(shuō)出所有問(wèn)題的細(xì)節(jié),就可直接跳過(guò)了,否則下文還是值得一看的:

  • 一問(wèn):JS的Number類型能安全表達(dá)的最大整型數(shù)值是多少?為什么(注意要求更嚴(yán),是安全表達(dá))?
  • 二問(wèn):在Long取值范圍內(nèi),2的指數(shù)次整數(shù)轉(zhuǎn)換為JS的Number類型,不會(huì)有精度丟失,但能放心使用么?
  • 三問(wèn):我們一般都知道十進(jìn)制數(shù)轉(zhuǎn)二進(jìn)制浮點(diǎn)數(shù)有可能會(huì)出現(xiàn)精度丟失,但精度丟失具體怎么發(fā)生的?
  • 四問(wèn):如果不幸中招,服務(wù)端正在使用Long類型作為大整數(shù)的返回,有哪些辦法解決?

基礎(chǔ)回顧

在解答上面這些問(wèn)題前,先介紹本文涉及到的重要基礎(chǔ):IEEE754浮點(diǎn)數(shù)標(biāo)準(zhǔn)。如果大家對(duì)IEEE754的細(xì)節(jié)爛熟于心的話,可以跳過(guò)本段內(nèi)容,直接看下一段,問(wèn)題解答部分。

當(dāng)前業(yè)界流行的浮點(diǎn)數(shù)標(biāo)準(zhǔn)是IEEE754,該標(biāo)準(zhǔn)規(guī)定了4種浮點(diǎn)數(shù)類型:單精度、雙精度、延伸單精度、延伸雙精度。前兩種類型是最常用的。我們單介紹一下雙精度,掌握雙精度,自然就了解了單精度(而且上述問(wèn)題場(chǎng)景也是涉及雙精度)。

雙精度分配了8個(gè)字節(jié),總共64位,從左至右劃分是1位符號(hào)、11位指數(shù)、52位有效數(shù)字。如下圖所示,以0.7為例,展示了雙精度浮點(diǎn)數(shù)的存儲(chǔ)方式。

 

存儲(chǔ)位分配

1)符號(hào)位:在最高二進(jìn)制位上分配1位表示浮點(diǎn)數(shù)的符號(hào),0表示正數(shù),1表示負(fù)數(shù)。

2)指數(shù):也叫階碼位。

在符號(hào)位右側(cè)分配11位用來(lái)存儲(chǔ)指數(shù),IEEE754標(biāo)準(zhǔn)規(guī)定階碼位存儲(chǔ)的是指數(shù)對(duì)應(yīng)的移碼,而不是指數(shù)的原碼或補(bǔ)碼。根據(jù)計(jì)算機(jī)組成原理中對(duì)移碼的定義可知,移碼是將一個(gè)真值在數(shù)軸上正向平移一個(gè)偏移量之后得到的,即[x]移=x+2^(n-1)(n為x的二進(jìn)制位數(shù),含符號(hào)位)。移碼的幾何意義是把真值映射到一個(gè)正數(shù)域,其特點(diǎn)是可以直觀地反映兩個(gè)真值的大小,即移碼大的真值也大。基于這個(gè)特點(diǎn),對(duì)計(jì)算機(jī)來(lái)說(shuō)用移碼比較兩個(gè)真值的大小非常簡(jiǎn)單,只要高位對(duì)齊后逐個(gè)比較即可,不用考慮負(fù)號(hào)的問(wèn)題,這也是階碼會(huì)采用移碼表示的原因所在。

由于階碼實(shí)際存儲(chǔ)的是指數(shù)的移碼,所以指數(shù)與階碼之間的換算關(guān)系就是指數(shù)與它的移碼之間的換算關(guān)系。假設(shè)指數(shù)的真值為e,階碼為E ,則有 E = e + (2 ^ (n-1) - 1),其中 2 ^ (n-1) - 1 是IEEE754 標(biāo)準(zhǔn)規(guī)定的偏移量。則雙精度下,偏移量為1023,11位二進(jìn)制取值范圍為[0,2047],因?yàn)槿?是機(jī)器零、全1是無(wú)窮大都被當(dāng)做特殊值處理,所以E的取值范圍為[1,2046],減去偏移量,可得e的取值范圍為[-1022,1023] 。

3)有效數(shù)字:也叫尾數(shù)位。最右側(cè)分配連續(xù)的52位用來(lái)存儲(chǔ)有效數(shù)字,IEEE754標(biāo)準(zhǔn)規(guī)定尾數(shù)以原碼表示。

浮點(diǎn)數(shù)和十進(jìn)制之間的轉(zhuǎn)換

在實(shí)際實(shí)現(xiàn)中,浮點(diǎn)數(shù)和十進(jìn)制之間的轉(zhuǎn)換規(guī)則有3種情況:

1 規(guī)格化

指數(shù)位不是全零,且不是全1時(shí),有效數(shù)字最高位前默認(rèn)增加1,不占用任何比特位。那么,轉(zhuǎn)十進(jìn)制計(jì)算公式為:

  1. (-1)^s*(1+m/2^52)*2^(E-1023) 

其中s為符號(hào),m為尾數(shù),E為階碼。比如上圖中的0.7 :

1)符號(hào)位:是0,代表正數(shù)。

2)指數(shù)位:01111111110,轉(zhuǎn)換為十進(jìn)制,得階碼E為1022,則真值e=1022-1023=-1。

3)有效數(shù)字:

  1. 0110011001100110011001100110011001100110011001100110 

轉(zhuǎn)換為十進(jìn)制,尾數(shù)m為:1801439850948198。

4)計(jì)算結(jié)果:

(1+1801439850948198/2^52)*(2^-1) =0.6999999999999999555910790149937383830547332763671875

經(jīng)過(guò)顯示優(yōu)化算法后(在后文中詳述),為0.7。

2 非規(guī)格化

指數(shù)位是全零時(shí),有效數(shù)字最高位前默認(rèn)為0。那么,轉(zhuǎn)十進(jìn)制計(jì)算公式:

  1. (-1)^s*(0+m/2^52)*2^(-1022) 

注意,指數(shù)位是-1022,而不是-1023,這是為了平滑有效數(shù)字最高位前沒(méi)有1。比如非規(guī)格最小正值為:

  1. 0x0.0000000000001*2^-1022=2^-52 * 2^-1022 = 4.9*10^-324 

3 特殊值

指數(shù)全為1,有效數(shù)字全為0時(shí),代表無(wú)窮大;有效數(shù)字不為0時(shí),代表NaN(不是數(shù)字)。

問(wèn)題解答

1 JS的Number類型能安全表達(dá)的最大整型數(shù)值是多少?為什么?

規(guī)約中已經(jīng)指出:

在Long類型能表示的最大值是2的63次方-1,在取值范圍之內(nèi),超過(guò)2的53次方(9007199254740992)的數(shù)值轉(zhuǎn)化為JS的Number時(shí),有些數(shù)值會(huì)有精度損失。

“2的53次方”這個(gè)限制是怎么來(lái)的呢?如果看懂上文IEEE754基礎(chǔ)回顧,不難得出:在浮點(diǎn)數(shù)規(guī)格化下,雙精度浮點(diǎn)數(shù)的有效數(shù)字有52位,加上有效數(shù)字最高位前默認(rèn)為1,共53位,所以JS的Number能保障無(wú)精度損失表達(dá)的最大整數(shù)是2的53次方。

而這里的題問(wèn)是:“能安全表達(dá)的最大整型”,安全表達(dá)的要求,除了能準(zhǔn)確表達(dá),還有正確比較。2^53=9007199254740992,實(shí)際上,

  1. 9007199254740992+1 == 9007199254740992 

的比較結(jié)果為true。如下圖所示:

 

這個(gè)測(cè)試結(jié)果足以說(shuō)明2^53不是一個(gè)安全整數(shù),因?yàn)樗荒芪ㄒ淮_定一個(gè)自然整數(shù),實(shí)際上9007199254740992、9007199254740993,都對(duì)應(yīng)這個(gè)值。因此這個(gè)問(wèn)題的答案是:2^53-1。

2 在Long取值范圍內(nèi),2的指數(shù)次整數(shù)轉(zhuǎn)換為JS的Number類型,不會(huì)有精度丟失,但能放心使用么?

規(guī)約中指出:

在Long取值范圍內(nèi),任何2的指數(shù)次整數(shù)都是絕對(duì)不會(huì)存在精度損失的,所以說(shuō)精度損失是一個(gè)概率問(wèn)題。若浮點(diǎn)數(shù)尾數(shù)位與指數(shù)位空間不限,則可以精確表示任何整數(shù)。

后半句,我們就不說(shuō)了,因?yàn)榻^對(duì)沒(méi)毛病,空間不限,不僅是任何整數(shù)可以精確表示,無(wú)理數(shù)我們也可以挑戰(zhàn)一下。我們重點(diǎn)看前半句,根據(jù)本文前面所述基礎(chǔ)回顧,雙精度浮點(diǎn)數(shù)的指數(shù)取值范圍為[-1022,1023],而指數(shù)是以2為底數(shù)。另外,雙精度浮點(diǎn)數(shù)的取值范圍,比Long大,所以,理論上Long型變量中2的指數(shù)次整數(shù)一定可以準(zhǔn)確轉(zhuǎn)換為JS的umber類型。但在JS中,實(shí)際情況,卻是下面這樣:

 

2的55次方的準(zhǔn)確計(jì)算結(jié)果是:36028797018963968,而從上圖可看到,JS的計(jì)算結(jié)果是:36028797018963970。而且直接輸入36028797018963968,控制臺(tái)顯示結(jié)果是36028797018963970。

這個(gè)測(cè)試結(jié)果,已經(jīng)對(duì)本問(wèn)題給出答案。為了確保程序準(zhǔn)確,本文建議,在整數(shù)場(chǎng)景下,對(duì)于JS的Number類型使用,嚴(yán)格限制在2^53-1以內(nèi),最好還是信規(guī)約的,直接使用String類型。

為什么會(huì)出現(xiàn)上面的測(cè)試現(xiàn)象呢?

實(shí)際上,我們?cè)诔绦蛑休斎胍粋€(gè)浮點(diǎn)數(shù)a,在輸出得到a',會(huì)經(jīng)歷以下過(guò)程:

1)輸入時(shí):按照IEEE754規(guī)則,將a存儲(chǔ)。這個(gè)過(guò)程很有可能會(huì)發(fā)生精度損失。

2)輸出時(shí):按照IEEE754規(guī)則,計(jì)算a對(duì)應(yīng)的值。根據(jù)計(jì)算結(jié)果,尋找一個(gè)最短的十進(jìn)制數(shù)a',且要保障a'不會(huì)和a隔壁浮點(diǎn)數(shù)的范圍沖突。a隔壁浮點(diǎn)數(shù)是什么意思呢?由于存儲(chǔ)位數(shù)是限定的,浮點(diǎn)數(shù)其實(shí)是一個(gè)離散的集合,兩個(gè)緊鄰的浮點(diǎn)數(shù)之間,還存在著無(wú)數(shù)的自然數(shù)字,無(wú)法表達(dá)。假設(shè)有f1、f2、f3三個(gè)升序浮點(diǎn)數(shù),且它們之間的距離,不可能在拉近。則在這三個(gè)浮點(diǎn)數(shù)之間,按照范圍來(lái)劃分自然數(shù)。而浮點(diǎn)數(shù)輸出的過(guò)程,就是在自己范圍中找一個(gè)最適合的自然數(shù),作為輸出。如何找到最合適的自然數(shù),這是一個(gè)比較復(fù)雜的浮點(diǎn)數(shù)輸出算法,大家感興趣的,可參考相關(guān)論文[1]。

 

所以,36028797018963968和36028797018963970這兩個(gè)自然數(shù),對(duì)應(yīng)到計(jì)算機(jī)浮點(diǎn)數(shù)來(lái)說(shuō),其實(shí)是同一個(gè)存儲(chǔ)結(jié)果,雙精度浮點(diǎn)數(shù)無(wú)法區(qū)分它們,最終呈現(xiàn)哪一個(gè)十進(jìn)制數(shù),就看浮點(diǎn)數(shù)的輸出算法了。下圖這個(gè)例子可以說(shuō)明這兩個(gè)數(shù)字在浮點(diǎn)數(shù)中是相等的。另外,大家可以想想輸入0.7,輸出是0.7的問(wèn)題,浮點(diǎn)數(shù)是無(wú)法精確存儲(chǔ)0.7,輸出卻能夠精確,也是因?yàn)橛懈↑c(diǎn)數(shù)輸出算法控制(特別注意,這個(gè)輸出算法無(wú)法保證所有情況下,輸入等于輸出,它只是盡力確保輸出符合正常的認(rèn)知)。

 

擴(kuò)展

JS的Number類型既用來(lái)做整數(shù)計(jì)算、也用來(lái)做浮點(diǎn)數(shù)計(jì)算。其轉(zhuǎn)換為String輸出的規(guī)則也會(huì)影響我們使用,具體規(guī)則如下:

上面是一段典型的又臭又長(zhǎng)但邏輯很嚴(yán)謹(jǐn)?shù)拿枋觯铱偨Y(jié)了一個(gè)不是很嚴(yán)謹(jǐn),但好理解的說(shuō)法,大家可以參考一下:

 

除了小數(shù)點(diǎn)前的數(shù)字位數(shù)(不算開(kāi)始的0)少于22位,且絕對(duì)值大于等于1e-6的情況,其余都用科學(xué)計(jì)數(shù)法格式化輸出。舉例:

 

3 我們一般都知道十進(jìn)制數(shù)轉(zhuǎn)二進(jìn)制浮點(diǎn)數(shù)有可能會(huì)出現(xiàn)精度丟失,精度丟失怎么發(fā)生的?

通過(guò)前面IEEE754分析,我們知道十進(jìn)制數(shù)存儲(chǔ)到計(jì)算機(jī),需要轉(zhuǎn)換為二進(jìn)制。有兩種情況,會(huì)導(dǎo)致轉(zhuǎn)換后精度損失:

1)轉(zhuǎn)換結(jié)果是無(wú)限循環(huán)數(shù)或無(wú)理數(shù)

比如0.1轉(zhuǎn)換成二進(jìn)制為:

  1. 0.0001 10011001100110011001100110011... 

其中0011在循環(huán)。將0.1轉(zhuǎn)換為雙精度浮點(diǎn)數(shù)二進(jìn)制存儲(chǔ)為:

  1. 0 01111111011 1001100110011001100110011001100110011001100110011001 

按照本文前面所述基礎(chǔ)回顧中的計(jì)算公式 (-1)^s*(1+m/2^52)*2^(E-1023)計(jì)算,可得轉(zhuǎn)換回十進(jìn)制為:0.09999999999999999。這里可以看出,浮點(diǎn)數(shù)有時(shí)是無(wú)法精確表達(dá)一個(gè)自然數(shù),這個(gè)和十進(jìn)制中1/3 =0.333333333333333...是一個(gè)道理。

2)轉(zhuǎn)換結(jié)果長(zhǎng)度,超過(guò)有效數(shù)字位數(shù),超過(guò)部分會(huì)被舍棄

IEEE754默認(rèn)是舍入到最近的值,如果“舍”和“入”一樣接近,那么取結(jié)果為偶數(shù)的選擇。

另外,在浮點(diǎn)數(shù)計(jì)算過(guò)程中,也可能引起精度丟失。比如,浮點(diǎn)數(shù)加減運(yùn)算執(zhí)行步驟分為:

零值檢測(cè) -> 對(duì)階操作 -> 尾數(shù)求和 -> 結(jié)果規(guī)格化 -> 結(jié)果舍入

其中對(duì)階和規(guī)格化都有可能造成精度損失:

  • 對(duì)階:是通過(guò)尾數(shù)右移(左移會(huì)導(dǎo)致高位被移出,誤差更大,所以只能是右移),將小指數(shù)改成大指數(shù),達(dá)到指數(shù)階碼對(duì)齊的效果,而右移出的位,會(huì)作為保護(hù)位暫存,在結(jié)果舍入中處理,這一步有可能導(dǎo)致精度丟失。
  • 規(guī)格化:是為了保障計(jì)算結(jié)果的尾數(shù)最高位是1,視情況有可能會(huì)出現(xiàn)右規(guī),即將尾數(shù)右移,從而導(dǎo)致精度丟失。

4 如果不幸中招,服務(wù)端正在使用Long類型作為大整數(shù)的返回,有哪些辦法解決?

需要分情況。

1)通過(guò)Web的ajax異步接口,以Json串的形式返回給前端

方案一:如果,返回Long型所在的POJO對(duì)象在其他地方無(wú)使用,那么可以將后端的Long型直接修改成String型。

方案二:如果,返回給前端的Json串是將一個(gè)POJO對(duì)象Json序列化而來(lái),并且這個(gè)POJO對(duì)象還在其他地方使用,而無(wú)法直接將其中的Long型屬性直接改為String,那么可以采用以下方式:

  1. String orderDetailString = JSON.toJSONString(orderVO, SerializerFeature.BrowserCompatible); 

SerializerFeature.BrowserCompatible 可以自動(dòng)將數(shù)值變成字符串返回,解決精度問(wèn)題。

方案三:如果,上述兩種方式都不適合,那么這種方式就需要后端返回一個(gè)新的String類型,前端使用新的,并后續(xù)上線后下掉老的Long型(推薦使用該方式,因?yàn)榭梢悦鞔_使用String型,防止后續(xù)誤用Long型)。

2)使用node的方式,直接通過(guò)調(diào)用后端接口的方式獲取

方案一:使用npm的js-2-java的 java.Long(orderId) 方法兼容一下。

方案二:后端接口返回一個(gè)新的String類型的訂單ID,前端使用新的屬性字段(推薦使用,防止后續(xù)踩坑)。

引用

[1]http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.52.2247&rank=2

 

[2]《碼出高效》

 

 

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

2013-07-15 15:17:24

2011-08-10 09:30:14

云計(jì)算

2025-03-26 03:00:00

2017-07-10 13:38:07

MySQL數(shù)據(jù)類型整數(shù)類型

2024-02-05 14:12:37

大模型RAG架構(gòu)

2025-04-01 08:10:00

JavaScripteval()函數(shù)代碼

2023-06-07 08:22:59

LLM微調(diào)技術(shù)

2020-09-14 09:47:56

Java開(kāi)發(fā)類型

2012-05-04 17:09:40

華為手機(jī)小米手機(jī)

2016-10-10 22:48:16

2020-05-06 12:24:57

NPE三目運(yùn)算符

2009-12-01 16:49:05

VS2003安裝

2015-05-27 09:58:09

2010-11-26 11:00:54

職場(chǎng)

2011-05-13 09:19:15

無(wú)線路由器無(wú)線網(wǎng)絡(luò)

2025-06-23 07:54:40

2021-10-03 22:18:14

Go語(yǔ)言整數(shù)

2024-05-06 07:58:23

MoE模型系統(tǒng)

2023-10-06 20:30:33

大模型LLMtoken

2011-02-22 13:26:01

華為3Leaf收購(gòu)
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 一区二区三区免费 | 亚洲精品久久区二区三区蜜桃臀 | 免费超碰 | 国产成都精品91一区二区三 | a黄毛片| 99riav国产一区二区三区 | 国产精品欧美一区二区 | 拍真实国产伦偷精品 | 欧美日韩网站 | 天堂在线免费视频 | 超碰日韩 | 免费在线观看一区二区 | 黄色网址在线免费观看 | 国产精品久久亚洲7777 | 一级大片免费 | 日本在线小视频 | 国产激情一区二区三区 | 免费一区二区 | 成人免费看黄网站在线观看 | 在线视频 亚洲 | 91视频在线 | 久久国产欧美日韩精品 | 精品av| 久草视频在线播放 | 中文字幕免费在线 | 国产一区二区三区在线 | 一区二区三区四区不卡视频 | 少妇午夜一级艳片欧美精品 | 国产欧美综合在线 | 9porny九色视频自拍 | av一区二区在线观看 | 亚洲精品欧美精品 | 欧美日韩一区精品 | 在线不卡视频 | 国产亚洲一区二区三区 | 91久久精品国产91久久性色tv | 男人的天堂avav| 日韩免费一区二区 | 亚洲成人播放器 | 亚洲精品国产精品国自产在线 | 成年网站在线观看 |