js long类型精度丢失_浮点数丢失精度
問題
浮點數在運算過程中常常會丟失精度,這是由于二進制數的存儲特點造成的,在php或者js中進行浮點數運算或者類型轉換的時候常常會丟失精度。而在電商公司,對金額比較敏感,是萬萬不能接受絲毫的誤差的。
看下面這段代碼,它的運行結果分別是什么呢?
你的答案可能是
29890 29890 29890如果真是這樣,也就沒必要特意提出來說了,其實運行結果是這樣的
29890 29889 29890為什么第二個值變成了29889呢?這和預期不符
讓我們來分析一下這個問題,下面這段js代碼,可以copy到控制臺運行一下
var a = 289.90; console.log(a * 100); console.log(parseInt(a * 100)); console.log(a * 100 + '');運行結果如下
28989.999999999996 28989 28989.999999999996運行上面這段js代碼,結果是浮點數經過乘法運算之后得出的值已經是略小于真實值了,原因是計算機是以二進制數處理數字的,進行運算之后由于長度限制會丟失精度。而經過強制類型轉換,變成整型會截取非數字前的部分,就比如運行下面的代碼結果會是數值289和數值-289。
var a = '289abc'; var b = '-289abc'; console.log(parseInt(a)); console.log(parseInt(b));運行結果
289 -289這樣就可以解釋為啥開頭的例子里第二個數是29889了。
再來看另一個例子
$var1 = 298.90; $var2 = $var1 * 100;$var3 = (int)($var2.'');$var4 = (string)$var2;echo $var2; echo $var3; echo $var4;運行結果是
29890 29890 29890就因為轉化成了字符串,就一切如常了。
為什么會這樣呢?這里我也不太清楚原理,查閱資料也沒有弄清楚,希望有知道的同學留言解答一下!
那精度丟失的問題到底有多嚴重,什么時候我們需要注意呢?我們可以寫幾個demo來大概了解一下。最常遇到的運算就是“元”和“分”的相互轉換。
在js里將0.01元 到100元之間的10000個數值,分別轉化成分
var right = 0,error = 0,j = 100; for(var i = 0;i < 100;i = (parseFloat(i) + 0.01).toFixed(2)){var res = i * j;if(res != res.toFixed(2)){error ++;console.log(i + ' * ' + j + ' = ' + res);}else{right ++;} } console.log('right: ' + right); console.log('error: ' + error); console.log('over');結果如下
... 81.46 * 100 = 8145.999999999999 81.49 * 100 = 8148.999999999999 81.51 * 100 = 8151.000000000001 81.54 * 100 = 8154.000000000001 81.57 * 100 = 8156.999999999999 81.60 * 100 = 8159.999999999999 81.65 * 100 = 8165.000000000001 81.68 * 100 = 8168.000000000001 81.71 * 100 = 8170.999999999999 81.74 * 100 = 8173.999999999999 81.76 * 100 = 8176.000000000001 81.79 * 100 = 8179.000000000001 81.82 * 100 = 8181.999999999999 81.85 * 100 = 8184.999999999999 81.90 * 100 = 8190.000000000001 right: 8854 error: 1146 over在js里將1分到10000分之間的10000個數值,分別轉化成元
var right = 0,error = 0,j = 100; for(var i = 0;i < 10000;i = Math.round(parseInt(i) + 1)){var res = i / j;if(res != res.toFixed(2)){error ++;console.log(i + ' / ' + j + ' = ' + res);}else{right ++;} } console.log('right: ' + right); console.log('error: ' + error); console.log('over');結果如下
right: 10000 error: 0 over在js里使兩個0到1之間的兩位小數相減
var right = 0,error = 0; for(var i = 0;i < 1;i = (parseFloat(i) + 0.01).toFixed(2)){for(var j = 0;j < 1;j = (parseFloat(j) + 0.01).toFixed(2)){var res = parseFloat(i) - parseFloat(j);if(res != res.toFixed(2)){error ++;console.log(i + ' - ' + j + ' = ' + res);}else{right ++;}} } console.log('right: ' + right); console.log('error: ' + error); console.log('over');結果如下
... 0.99 - 0.83 = 0.16000000000000003 0.99 - 0.84 = 0.15000000000000002 0.99 - 0.88 = 0.10999999999999999 0.99 - 0.89 = 0.09999999999999998 0.99 - 0.90 = 0.08999999999999997 0.99 - 0.91 = 0.07999999999999996 0.99 - 0.92 = 0.06999999999999995 0.99 - 0.93 = 0.05999999999999994 0.99 - 0.94 = 0.050000000000000044 0.99 - 0.95 = 0.040000000000000036 0.99 - 0.96 = 0.030000000000000027 0.99 - 0.97 = 0.020000000000000018 0.99 - 0.98 = 0.010000000000000009 right: 4844 error: 5156 over在js里使兩個0到1之間的兩位小數相加
var right = 0,error = 0; for(var i = 0;i < 1;i = (parseFloat(i) + 0.01).toFixed(2)){for(var j = 0;j < 1;j = (parseFloat(j) + 0.01).toFixed(2)){var res = parseFloat(i) + parseFloat(j);if(res != res.toFixed(2)){error ++;console.log(i + ' + ' + j + ' = ' + res);}else{right ++;}} } console.log('right: ' + right); console.log('error: ' + error); console.log('over');結果如下
... 0.99 + 0.12 = 1.1099999999999999 0.99 + 0.35 = 1.3399999999999999 0.99 + 0.37 = 1.3599999999999999 0.99 + 0.40 = 1.3900000000000001 0.99 + 0.58 = 1.5699999999999998 0.99 + 0.60 = 1.5899999999999999 0.99 + 0.62 = 1.6099999999999999 0.99 + 0.65 = 1.6400000000000001 0.99 + 0.67 = 1.6600000000000001 0.99 + 0.83 = 1.8199999999999998 0.99 + 0.85 = 1.8399999999999999 0.99 + 0.87 = 1.8599999999999999 0.99 + 0.90 = 1.8900000000000001 0.99 + 0.92 = 1.9100000000000001 right: 7894 error: 2106 over在這些例子里,出錯的值占到了很高的比例,但錯誤值和真實值之間的誤差非常小,四舍五入就可以避免。我們在處理數值運算時一定要注意進行處理。
總結
1 如果遇到精度丟失,最簡單的辦法就是四舍五入
//php方法 $lDefSupPrice = round(79.60 * 100);//取整 $lDefSupPrice = sprintf("%.2f", (0.99 + 0.92));//保留兩位小數 //js方法 var fPrice = Math.round(79.60 * 100);//取整 var fPrice = (0.99 + 0.92).toFixed(2);//保留兩位小數2 將整數部分與小數部分分開分別運算,例如
define("float.operation", function(require, exports, module) {//加法Number.prototype.add = function(arg){var r1,r2,m;try{r1=this.toString().split(".")[1].length}catch(e){r1=0}try{r2=arg.toString().split(".")[1].length}catch(e){r2=0}m=Math.pow(10,Math.max(r1,r2))return (Number((this*m).toFixed())+Number((arg*m).toFixed()))/m}//減法Number.prototype.sub = function (arg){return this.add(-arg);}//乘法Number.prototype.mul = function (arg){var m=0,s1=this.toString(),s2=arg.toString();try{m+=s1.split(".")[1].length}catch(e){}try{m+=s2.split(".")[1].length}catch(e){}return Number(s1.replace(".",""))*Number(s2.replace(".",""))/Math.pow(10,m)}//除法Number.prototype.div = function (arg){var t1=0,t2=0,r1,r2;try{t1=this.toString().split(".")[1].length}catch(e){}try{t2=arg.toString().split(".")[1].length}catch(e){}with(Math){r1=Number(this.toString().replace(".",""))r2=Number(arg.toString().replace(".",""))return (r1/r2)*pow(10,t2-t1);}} });3 如果遇到在調接口的時候php傳到接口的時候是準確的,后臺讀取的時候出錯了,可以以字符串的形式來傳這個字段,因為PHP是弱類型語言,現在我們的接口大多數情況允許類型不準確
4 在用php處理excel、csv等表格的時候,也可能遇到數據類型的問題,例如生成表格的時候如果以字符串形式存大數字(例如手機號、訂單號、身份證號),默認會以科學計數法來顯示,甚至身份證號精確度不夠直接將后幾位置為0了,在前面拼接上空格或英文單引號’以字符串形式輸出,在表格里就能正確顯示了
5 對于訂單號和手機驗證碼之類可能以0開頭的數字,千萬不能轉整型,另外也不能用 getValueI()方法
$this ->getValueI();6 對于較大數字的運算,例如解析和拼裝后臺的屬性標,不建議在js中運算,容易溢出,在php中運算會有所改善,附上php解析屬性標的方法
/**** 解析訂單屬性lTradeProperty1標簽* 入參是屬性標* 回參是一個包含屬性標字符串的數組*/ function getTradeProperty1($val=NULL){$skuPropertyNew=array(0x000000001 => 'change', //參加以舊換新活動0x000000002 => 'coupon', //使用優惠券0x000000004 => 'presell', //預售商品(只推遲發貨)0x000000008 => 'limit', //限時優惠0x000000010 => 'score', //積分抵扣0x000000020 => 'gift', //禮品券0x000000040 => 'newtest', //新品試用0x000000080 => 'presellmoney', //預售商品(要交定金) );$arrProperty=array();foreach($skuPropertyNew as $k=>$v) {if($val & $k) {$arrProperty[]=$v;}}return $arrProperty; } 歡迎補充!參考:
PHP浮點數的一個常見問題的解答
關于PHP浮點數你應該知道的
總結
以上是生活随笔為你收集整理的js long类型精度丢失_浮点数丢失精度的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 矿棉板吊顶工费价格?
- 下一篇: 关系到了冰点_疫情下半场,如何修复跌至冰