質問

Math.floor()を使用する代わりに、数値を.5だけ右シフトできると聞きました。適切な代替品であることを確認するためにその制限を確認することにしました。そこで、Google Chromeで次の値を確認し、次の結果を得ました。


2.5 >> .5 == 2;
2.9999 >> .5 == 2;
2.999999999999999 >> .5 == 2;  // 15 9s
2.9999999999999999 >> .5 == 3;  // 16 9s

いじくり回した後、0.5だけ右にシフトすると2になる2の最大値は2.9999999999999997779553950749686919152736663818359374999999¯であることがわかりました。 (9回繰り返し)ChromeとFirefoxで。番号は2.9999999999999997779¯です。 IEで。

私の質問は:.0000000000000007779553950749686919152736663818359374という数字の意味は何ですか?とても奇妙な数字で、私の好奇心をそそりました。

答えまたは少なくとも何らかのパターンを見つけようとしましたが、私の問題はビット単位の操作を本当に理解していないという事実にあると思います。私は原則としてこの考え方を理解していますが、ビットシーケンスを0.5シフトしても意味がありません。助けていただければ幸いです。

レコードの場合、奇妙な数字列は2 ^ xで変化します。引き続き適切に切り捨てられる次の数値の可能な最大値:

for 0: 0.9999999999999999444888487687421729788184165954589843749¯
for 1: 1.9999999999999999888977697537484345957636833190917968749¯
for 2-3: x+.99999999999999977795539507496869191527366638183593749¯
for 4-7: x+.9999999999999995559107901499373838305473327636718749¯
for 8-15: x+.999999999999999111821580299874767661094665527343749¯
...and so forth
役に立ちましたか?

解決

実際には、第1オペランドでfloor()を実行するだけで、浮動小数点演算は実行されません。左シフトと右シフトのビット演算は整数オペランドでのみ意味があるため、JavaScriptエンジンは最初に2つのオペランドを整数に変換しています:

2.999999 >> 0.5

なる:

Math.floor(2.999999) >> Math.floor(0.5)

順番は次のとおりです:

2 >> 0

0ビットのシフトは、「シフトを行わない」ことを意味します。そのため、整数に切り捨てられた第1オペランドになります。

SpiderMonkeyのソースコードは次のとおりです。

switch (op) {
  case JSOP_LSH:
  case JSOP_RSH:
    if (!js_DoubleToECMAInt32(cx, d, &i)) // Same as Math.floor()
        return JS_FALSE;
    if (!js_DoubleToECMAInt32(cx, d2, &j)) // Same as Math.floor()
        return JS_FALSE;
    j &= 31;
    d = (op == JSOP_LSH) ? i << j : i >> j;
    break;

「切り上げ」が表示されますJavaScriptエンジンは特定の精度を超える小数を処理できないため、特定の数値を使用する場合、数値は次の整数に切り上げられます。ブラウザでこれを試してください:

alert(2.999999999999999);

2.999999999999999が得られます。さらに9を追加してみてください:

alert(2.9999999999999999);

3を取得します。

他のヒント

これは、おそらく私が見た中で最悪の考えです。既存の唯一の可能な目的は、難読化されたコードコンテストに勝つことです。あなたが投稿した長い数字には何の意味もありません-それらは基礎となる浮動小数点実装のアーティファクトであり、神が知っている中間層の数によってフィルタリングされます。小数バイトのビットシフトは非常識であり、例外を発生させないことに驚いていますが、それはJavascriptであり、常に「非常識」を再定義してくれます。

もし私があなただったら、この「機能」を使わないようにしましょう。その唯一の値は、異常なエラー状態の根本的な原因として考えられます。 Math.floor()を使用して、コードを保守する次のプログラマーに同情します。


質問を読んだときに私が抱いたいくつかの疑いを確認する:

  • 任意の小数 x を任意の小数 y で右シフトすると、単に x が切り捨てられ、 Mathと同じ結果が得られます。 .floor()を読みながら読者を完全に混乱させます。
  • 2.999999999999999777955395074968691915 ...は、単に「3」と区別できる最大数です。単独で評価してみてください。何かを追加すると、3に評価されます。これは、ブラウザーとローカルシステムの浮動小数点実装のアーティファクトです。

さらに詳しく知りたい場合は、「すべてのコンピューター科学者が浮動小数点演算について知っておくべきこと」をお読みください: http://docs.sun.com/source/806-3568/ncg_goldberg.html

あなたの右のシフトは関係ないと思います。単に倍精度浮動小数点定数の解像度を超えています。

Chromeの場合:

var x = 2.999999999999999777955395074968691915273666381835937499999;
var y = 2.9999999999999997779553950749686919152736663818359375;

document.write("x=" + x);
document.write(" y=" + y);

印刷:x = 2.9999999999999996 y = 3

このJavaScriptを試してください:   alert(parseFloat(&quot; 2.9999999999999997779553950749686919152736663818359374999999&quot;));

次にこれを試してください:   alert(parseFloat(&quot; 2.9999999999999997779553950749686919152736663818359375&quot;));

見ているのは、単純な浮動小数点の不正確さです。詳細については、たとえば次を参照してください。 http://en.wikipedia.org/wiki / Floating_point#Accuracy_problems

基本的な問題は、2番目の数値を表すために浮動小数点値が取得できる最も近い値が3以上であるのに対し、floatが最初の数値に到達できる終了値が厳密に3未満であることです。

0.5で右シフトするのが正気である理由については、0.5自体が事前にint(0)に変換されているようです。その後、元のフロート(2.999 ...)は、通常どおり、切り捨てによってintに変換されます。

右シフト演算子は整数(両側)でのみ動作します。したがって、右に0.5ビットシフトすることは、右に0ビットシフトすることとまったく同じです。そして、左辺はシフト操作の前に整数に変換されます。これはMath.floor()と同じことをします。

  

2.9999999999999997779553950749686919152736663818359374999999の変換が疑われる   それにバイナリ表現は啓発的です。たぶんたった1ビット違います   真3から。

良い推測ですが、葉巻はありません。 倍精度FP番号には53ビットがあるため、実際には3の前の最後のFP番号は (正確):2.999999999999999555910790149937383830547332763671875

しかし、それはなぜですか 2.9999999999999997779553950749686919152736663818359375

(これは49999ではなく正確です...!)

最後の表示可能ユニットよりも高い?丸め。変換ルーチン(文字列から数値)は、入力を次の浮動小数点数に丸めるように正しくプログラムされています。

2.999999999999999555910790149937383830547332763671875

.......(間の値、増加)-&gt;切り捨て

2.9999999999999997779553950749686919152736663818359375

.......(間の値、増加)-&gt; 3に切り上げます

3

変換入力は完全な精度を使用する必要があります。数がちょうど半分の場合 それらの2つのfp番号(2.9999999999999997779553950749686919152736663818359375) 丸めは設定されたフラグに依存します。デフォルトの丸めは偶数に丸められます。つまり、数値は次の偶数に丸められます。

3 = 11.(バイナリ)

2.999 ... = 10.11111111111 ......(バイナリ)

すべてのビットが設定され、数は常に奇数です。つまり、正確な半数は切り上げられるため、3 ...

2.9999999999999997779553950749686919152736663818359374999999をそのバイナリ表現に変換することは啓発になると思います。たぶん、真の3とは1ビットだけ違います。

そして、ジョンの答えに加えて、これがMath.floorよりもパフォーマンスが高いというオッズは、ごくわずかです。

JavaScriptが浮動小数点数を使用するのか、ある種の無限精度ライブラリを使用するのかはわかりませんが、いずれにせよ、このような操作で丸めエラーが発生します-たとえ十分に定義されていても。

「。0000000000000007779553950749686919152736663818359374」という数字は、 イプシロンであり、&quot; E(1 + E )&gt; 1.&quot;

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top