13

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

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

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

私は答えまたは少なくともある種のパターンを見つけようとしていますが、私の問題は、ビット単位の操作を本当に理解していないという事実にあると思います。原理的には理解できますが、ビット シーケンスを 0.5 ずつシフトすることは、私にはまったく意味がありません。どんな助けでも大歓迎です。

記録のために、奇妙な数字シーケンスは 2^x で変わります。適切に切り捨てられる次の数値の可能な最大値:

0 の場合: 0.9999999999999999444888487687421729788184165954589843749¯
1 の場合: 1.9999999999999999888977697537484345957636833190917968749¯
2-3 の場合: x+.99999999999999977795539507496869191527366638183593749¯
4-7 の場合: x+.9999999999999995559107901499373838305473327636718749¯
8-15 の場合: x+.999999999999999111821580299874767661094665527343749¯
...など
4

10 に答える 10

63

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

2.999999 >> 0.5

なる:

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

これは次のとおりです。

2 >> 0

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

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 エンジンが特定の精度を超える 10 進数を処理できないため、数値が次の整数に切り上げられてしまうためです。ブラウザでこれを試してください:

alert(2.999999999999999);

2.999999999999999 が得られます。次に、もう 1 つ 9 を追加してみます。

alert(2.9999999999999999);

3が出ます。

于 2008-10-06T03:25:52.543 に答える
25

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

私があなただったら、この「機能」を使用することは絶対に避けます。その唯一の値は、異常なエラー状態の考えられる根本原因です。Math.floor()コードを維持する次のプログラマーを使用し、同情してください。


質問を読んだときに私が持っていたいくつかの疑いを確認します。

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

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

于 2008-10-06T03:35:42.370 に答える
5

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

Chrome の場合:

var x = 2.999999999999999777955395074968691915273666381835937499999;
var y = 2.9999999999999997779553950749686919152736663818359375;

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

出力: x = 2.9999999999999996 y=3

于 2008-10-06T02:53:08.150 に答える
5

この JavaScript を試してみてください:

次に、これを試してください:

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

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

0.5 だけ右にシフトすると、まったく正常に動作する理由については、0.5 自体が事前に int (0) に変換されているようです。次に、元の float (2.999...) は、通常どおり、切り捨てによって int に変換されます。

于 2008-10-06T02:53:57.303 に答える
3

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

于 2008-10-06T02:59:10.943 に答える
2

2.999999999999997779553950749686919152736663818359374999999 をバイナリ表現に変換することは賢明であると思います。おそらく、真の 3 とは 1 ビットしか違わないでしょう。

于 2008-10-10T14:28:29.293 に答える
2

2.999999999999997779553950749686919152736663818359374999999 をバイナリ表現に変換することは賢明だと思います。おそらく、真の 3 とは 1 ビットしか違わないでしょう。

推測ですが、葉巻はありません。倍精度浮動小数点数は 53 ビットなので、3 より前の最後の浮動小数点数は実際には (正確に) 2.99999999999999555910790149937383830547332763671875 です。

しかし、なぜ 2.999999999999997779553950749686919152736663818359375 なのか

(これは正確で、49999 ではありません... !)

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

2.999999999999999555910790149937383830547332763671875

.......(間の値、増加) -> 切り捨て

2.9999999999999997779553950749686919152736663818359375

....... (間の値、増加) -> 3 に切り上げ

3

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

3 = 11. (バイナリ)

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

すべてのビットが設定され、数値は常に奇数です。これは、正確な半分の数が切り上げられることを意味するため、奇妙な .....49999 ピリオドが得られます。これは、3 と区別できるように正確な半分よりも小さい必要があるためです。

于 2008-10-30T11:10:41.047 に答える
1

数値「.0000000000000007779553950749686919152736663818359374」はおそらくイプシロンであり、「(1 + E)>1となる最小の数値E」として定義されていることに注意してください。

于 2008-10-15T23:33:05.133 に答える
1

ジョンの答えに加えて、これが Math.floor よりもパフォーマンスが高い可能性はほとんどありません。

JavaScript が浮動小数点数を使用するのか、ある種の無限精度ライブラリを使用するのかはわかりませんが、いずれにせよ、このような操作では丸め誤差が発生します。

于 2008-10-06T02:54:06.180 に答える