簡體   English   中英

為什么模數運算符在javascript中返回小數?

[英]Why does modulus operator return fractional number in javascript?

為什么 JavaScript 中的49.90 % 0.10返回0.09999999999999581 我預計它是0。

因為 JavaScript 使用浮點數學,這會導致舍入錯誤。

如果您需要帶有兩位小數的精確結果,請在運算前將數字乘以100 ,然后再除以:

var result = ( 4990 % 10 ) / 100;

必要時圓。

Javascript 的 Number 使用“IEEE 雙精度”來存儲值。 它們無法准確存儲所有十進制數。 將十進制數轉換為二進制數時,由於舍入錯誤,結果不為零。

49.90 = 49.89999999999999857891452848...
 0.10 =  0.10000000000000000555111512...

因此 floor(49.90 / 0.10) 只有 498,余數將是 0.09999....


您似乎正在使用數字來存儲美元金額。 不要這樣做,因為浮點運算會傳播並放大舍入誤差。 將數字存儲為美分數量。 整數可以精確表示, 4990 % 10將返回 0。

我將把它留在這里以供將來參考,但這里有一個方便的函數,可以更精確地處理涉及浮點數的Remainder (因為JS 沒有模運算符)。

  function floatSafeRemainder(val, step){
    var valDecCount = (val.toString().split('.')[1] || '').length;
    var stepDecCount = (step.toString().split('.')[1] || '').length;
    var decCount = valDecCount > stepDecCount? valDecCount : stepDecCount;
    var valInt = parseInt(val.toFixed(decCount).replace('.',''));
    var stepInt = parseInt(step.toFixed(decCount).replace('.',''));
    return (valInt % stepInt) / Math.pow(10, decCount);
  }

 $(function() { function floatSafeModulus(val, step) { var valDecCount = (val.toString().split('.')[1] || '').length; var stepDecCount = (step.toString().split('.')[1] || '').length; var decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount; var valInt = parseInt(val.toFixed(decCount).replace('.', '')); var stepInt = parseInt(step.toFixed(decCount).replace('.', '')); return (valInt % stepInt) / Math.pow(10, decCount); } $("#form").submit(function(e) { e.preventDefault(); var safe = 'Invalid'; var normal = 'Invalid'; var var1 = parseFloat($('#var1').val()); var var2 = parseFloat($('#var2').val()); if (!isNaN(var1) && !isNaN(var2)) { safe = floatSafeModulus(var1, var2); normal = var1 % var2 } $('#safeResult').text(safe); $('#normalResult').text(normal); }); });
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <form id="form" novalidate> <div> <input type="number" id="var1">% <input type="number" id="var2"> </div> <div>safe: <span id="safeResult"></span><div> <div>normal (%): <span id="normalResult"></span></div> <input type="submit" value="try it out"> </form>

原因

浮點數無法准確存儲所有十進制值。 因此,當使用浮點格式時,輸入值總是會出現舍入錯誤。 輸入上的錯誤當然會導致輸出上的錯誤。 在離散函數或運算符的情況下,函數或運算符離散的點附近的輸出可能會有很大差異。 模運算符是離散的,您的情況顯然是此問題的一個示例。

浮點值的輸入和輸出

因此,在使用浮點變量時,您應該始終注意這一點。 並且無論您想要從浮點計算中獲得的任何輸出,在顯示之前都應始終進行格式化/條件化。
當只使用連續函數和運算符時,四舍五入到所需的精度通常會這樣做(不要截斷)。 用於將浮點數轉換為字符串的標准格式功能通常會為您執行此操作。
要根據輸入的預期精度和輸出的所需精度獲得正確的輸出,您還應該

  • 將輸入舍入到預期的精度,或確保不能以更高的精度輸入任何值。
  • 在舍入/格式化輸出之前向輸出添加一個小值,該值小於或等於所需精度的 1/4,並大於輸入和計算過程中舍入誤差引起的最大預期誤差。 如果這是不可能的,則所用數據類型的精度組合不足以為您的計算提供所需的輸出精度。

這 2 件事通常沒有完成,並且在大多數情況下,由於不執行它們而導致的差異對於大多數用戶來說太小了,以至於不重要,但是我已經有一個項目,如果沒有這些更正,用戶就不會接受輸出。

離散函數或運算符(如模數)

當涉及離散運算符或函數時,可能需要額外的修正以確保輸出符合預期。 四舍五入和在四舍五入之前添加小的更正不能解決問題。
可能需要在應用離散函數或運算符后立即對中間計算結果進行特殊檢查/更正。

這個問題的具體案例

在這種情況下,您希望輸入具有一定的精度,因此可以針對比所需精度小很多的舍入誤差的影響來校正輸出。

如果我們說您的數據類型的精度是 e。
您的輸入不會存儲為您輸入的值 a 和 b,而是存儲為 a*(1+/-e) 和 b*(1+/-e)
a*(1+/-e) 除以 b*(1+/-e) 的結果將導致 (a/b) (1+/-2e)。
modula 函數必須截斷結果並再次相乘。 所以結果將是 (a/b b)(1+/-3e) = a(1+/-3e) 導致 a*3e 的誤差。
該 mod 將 a*e 添加到 a*3e 的可能誤差中,因為減去了 2 個值,可能誤差為 a*3e 和 a*e。
因此,您應該檢查可能的總誤差 a*4e 是否小於所需的精度,如果滿足該條件並且結果與 b 的差異不超過最大可能誤差,則可以安全地將其替換為 0。

最好避免出現問題

通過使用數據類型(整數或定點格式)進行這樣的計算,可以更有效地避免這些問題,這樣可以存儲預期的輸入而不會出現舍入錯誤。 一個例子是你永遠不應該使用浮點值進行財務計算。

看看浮點數和它的缺點——像0.1這樣的數字不能正確保存為浮點數,所以總會出現這樣的問題。 取你的數字 *10 或 *100 並用整數進行計算。

http://en.wikipedia.org/wiki/Modulo_operation不要生氣模數與整數一起使用^^ 所以浮點值會出現一些錯誤。

這不是一個完美的答案,但它有效。

function format_float_bug(num)
{
   return parseFloat( num.toFixed(15) ); 
} 

你可以使用如下,

format_float_bug(4990 % 10);

因為下面的數字 (49.8999999999999857891452848) 前 15 個小數位就像 9999999

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM