どして 49.90 % 0.10
JavaScriptでの戻り値0.09999999999999581
? 0になると思っていました。
JavaScriptは浮動小数点演算を使用するため、常に丸め誤差が発生します。
小数点以下2桁の正確な結果が必要な場合は、演算の前に数値に100
を乗算し、その後で除算します。
var result = ( 4990 % 10 ) / 100;
必要に応じて丸めます。
JavaScriptの数値は、「IEEE倍精度」を使用して値を格納しています。すべての10進数を正確に格納することはできません。 10進数を2進数に変換するときの丸めエラーのため、結果はゼロではありません。
49.90 = 49.89999999999999857891452848...
0.10 = 0.10000000000000000555111512...
したがって、floor(49.90/0.10)はわずか498で、残りは0.09999になります。..
金額を保存するために数値を使用しているようです。 これを行わないでください 、浮動小数点演算が伝播し、丸め誤差を増幅するため。代わりに、数値をcentsの量として保存します。整数は正確に表すことができ、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>
原因
浮動小数点は、すべての小数値を正確に格納できません。したがって、浮動小数点形式を使用する場合、入力値には常に丸め誤差があります。もちろん、入力のエラーは出力のエラーになります。離散関数または演算子の場合、関数または演算子が離散しているポイントの周りの出力に大きな違いが生じる可能性があります。 modula演算子は離散的であり、あなたのケースは明らかにこの問題の例です。
浮動小数点値の入出力
そのため、浮動小数点変数を使用する場合は、常にこれに注意する必要があります。また、浮動小数点を使用した計算で必要な出力は、これを念頭に置いて表示する前に、常にフォーマット/条件付けする必要があります。
連続した関数と演算子のみが使用される場合、望ましい精度に丸めることがしばしば行われます(切り捨てないでください)。浮動小数点数を文字列に変換するために使用される標準のフォーマット機能は、通常これを行います。
予想される入力の精度と望ましい出力の精度に基づいて正しい出力を得るには、次のことも行う必要があります。
これら2つのことはしばしば行われず、ほとんどの場合、それらを行わないことによる差異はほとんどのユーザーにとって重要であるには小さすぎますが、これらの修正なしでは出力がユーザーに受け入れられないプロジェクトがすでにありました。
離散関数または演算子(modulaなど)
個別の演算子または関数が含まれる場合、出力が期待どおりであることを確認するには、追加の修正が必要になる場合があります。丸め、丸める前に小さな修正を追加しても問題を解決できません。
離散関数または演算子を適用した直後に、中間計算結果の特別なチェック/修正が必要になる場合があります。
この質問の特定のケース
この場合、特定の精度で入力を期待しているため、出力を修正して、希望する精度よりもはるかに小さい丸め誤差の影響を修正することができます。
データ型の精度がeであると言う場合。
入力は、入力した値aおよびbではなく、a *(1 +/- e)およびb *(1 +/- e)として保存されます
a *(1 +/- e)をb *(1 +/- e)で割った結果は、(a/b)(1 +/-2e)。
modula関数は、結果を切り捨てて再度乗算する必要があります。したがって、結果は(a/bb)(1 +/- 3e)= a(1 +/- 3e)となり、a * 3eのエラーが発生します。
a * 3eとa * eのエラーの可能性がある2つの値の減算のため、modはa * 3eのエラーの可能性にa * eを追加します。
そのため、考えられる合計エラーa * 4eが目的の精度よりも小さいことを確認する必要があります。その条件が満たされ、結果がbと最大の考えられるエラーを超えていない場合は、安全に0に置き換えることができます。 。
問題を回避する方が良い
多くの場合、このような計算にデータ型(整数または固定小数点形式)を使用することにより、これらの問題を回避する方が効率的です。その一例は、財務計算に浮動小数点値を使用してはならないことです。
floating points とその欠点を見てみましょう。0.1
のような数値は、浮動小数点として正しく保存できないため、常にこのような問題が発生します。 * 10または* 100の数値を受け取り、代わりに整数を使用して計算してください。
http://en.wikipedia.org/wiki/Modulo_operation 怒らないでくださいモジュロは整数で使用されます^^したがって、浮動値はいくつかのエラーが発生します。