3

私の質問の簡単なバージョンは次のとおりです。

x浮動小数点数が等しいと見なされる場合を決定するための「ベストプラクティス」とMath.round(x)見なされ、浮動小数点演算による精度の低下を許容するものは何ですか?


長いバージョンは次のとおりです。

特定の浮動小数点値xを「整数とみなす」べきか、もっと衒学的に言えば「整数の浮動小数点表現とみなす」べきかを決める必要があることがよくあります。

(たとえば、nが整数の場合、数式は

log 10 (10 n )

同じ整数nを表す複雑な方法です。これが、類似の浮動小数点計算の結果が「整数の表現」と見なされる可能性があると言う動機となる考え方です。)

Math.round(x) == xが に評価されるときはいつでも決定は簡単です: この場合、それは確かに整数 (の浮動小数点表現) であるtrueと言えます。x

しかし、テストMath.round(x) == xが に評価される場合、テストは決定的ではありませんfalse。例えば、

function log10(x) { return Math.log(x)/Math.LN10; }
// -> function()
x = log10(Math.pow(10, -4))
// -> -3.999999999999999
Math.round(x) == x
// -> false

編集: 私がよく目にする「解決策」の 1 つは、 のような任意の許容誤差を選択しε = 1e-6、 をテストすることですMath.abs(Math.round(x) - x) < ε。そのような解決策は、私が許容できるよりも多くの偽陽性を生み出すと思います.

4

3 に答える 3

5

あなたの例xでわかるように、実際には整数ではありません。これは、計算の早い段階での丸め誤差によるものであり、したがって、 がxほぼ丸められた数値として定義されたのか、それとも丸め誤差によってギザギザになった丸められた数値として定義されたのかを実際には知ることができません。

どちらの数値かを知りたい場合は、自分で提案した制限アプローチを使用するか、そもそも数値がぎざぎざにならないように十分に高いパーシジョンを使用する必要があります。この最後のアプローチは、すべての場合に適用できるわけではありません。

すべての数学演算をシンボリックに追跡する可能性もあります。つまり、手で式を評価するときのようにキャンセルできる要素をキャンセルしてオンデマンドで評価するのではなく保存1/3しますが、ほとんどの場合、これは完全にやり過ぎです。そのようなシステムがどれほど複雑になるかは言うまでもありません。これが望ましい解決策である場合は、おそらくMatLabまたはMathematicaまたは評価を処理する何かとインターフェースできますが、これをブラウザで実行するのがやや難しい場合を除き、WolframAlpha APIを試してください(なぜ私は考えなかったのですか?それが初めて?)。1/30.3333

それにもかかわらず; このような方法を選択することでこの問題を解決できる場合はε、満足のいく結果が得られます。これがおそらく最善の方法です。静的εにカットしない場合は、手元の数値に対して以前に行われた計算の種類に基づいて、動的に選択してみてください。つまり、乗算される数値は、除算される数値よりも分数部分が少なくなる傾向があります。数値がプラス、マイナス、および乗算 (分数を含まない) 以外に影響を受けていない場合は、小数点以下の最大桁数を知ることができるため、適切な を選択できεます。

于 2013-02-03T01:50:01.970 に答える
1

面白いので、ちょっと考えてみました。

これは1行のソリューションですが、数値型と文字列型の間の変換が含まれているため、最適かどうかはわかりません。ただし、最小しきい値を選択して、数値がその制限内にあるかどうかを確認するよりもはるかに正確です。

JavaScript の数値は倍精度の 64 ビット形式で、10 進数で約 16 桁の精度があります。これは、小数点以下の桁数だけでなく、合計桁数です。

JavaScript の数値には、数値を文字列に変換する toPrecision() メソッドもあり、指定された精度 (合計桁数なので、使用に適しています) に丸められます。次の例では、任意の数値を最も近い 15 桁の精度に丸めてから、浮動小数点数に変換します。

function roundToPrecision(number, precision) {
    return parseFloat(number.toPrecision(precision));
}

x = roundToPrecision(x, 15);

実際、あなたの例は整数になります: -4.

編集:もう少し考えた後、これはずっと速くなります:

var integerDigits = (""+parseInt(Math.abs(x))).length,
    threshold = 1e-16 * Math.pow(10, integerDigits);

Math.abs(Math.round(x) - x) < threshold

http://jsperf.com/number-precision-rounding

于 2013-02-03T06:36:39.717 に答える
1

xがゼロでないと仮定すると、比率を見る必要があると思いますMath.abs(x-Math.round(x))/x。これは、浮動小数点型がそれぞれ小数点以下の固定桁数ではなく、固定数の有効ビットを格納するという事実に対処しています。

次に、計算の典型的な丸め誤差を決定する必要があります。簡単な計算の結果ならx簡単かもしれません。そうでない場合は、正確な答えがわかっているテスト ケースから統計を収集することを検討してください。理想的には、整数の比率の最大値と、整数として扱われるべきではないx最小値との間に有意差があることがわかります。xもしそうなら、その範囲内のイプシロンを選んでください。

于 2013-02-03T07:23:54.323 に答える