JavaScript で浮動小数点を整数に変換する最良の方法は何ですか?

StackOverflow https://stackoverflow.com/questions/131406

  •  02-07-2019
  •  | 
  •  

質問

JavaScript で浮動小数点数を整数に変換するには、いくつかの異なる方法があります。私の質問は、どの方法が最高のパフォーマンスを提供するか、最も互換性があるか、またはベストプラクティスとみなされるかということです。

私が知っているいくつかの方法を次に示します。

var a = 2.5;
window.parseInt(a); // 2
Math.floor(a);      // 2
a | 0;              // 2

他にもきっといると思います。提案はありますか?

役に立ちましたか?

解決

によると このウェブサイト:

parseInt は、浮動小数点数を整数に変換する手段として使用されることがあります。引数が数値型の場合、まず文字列に変換されてから数値として解析されるため、このタスクには非常に適していません。

数値を整数に丸めるには、Math.round、Math.ceil、Math.floor のいずれかが推奨されます...

他のヒント

「JavaScript」より:良い部分」(ダグラス・クロックフォード著)

Number.prototype.integer = function () {
    return Math[this < 0 ? 'ceil' : 'floor'](this);
}

これを行うと、すべての Number オブジェクトにメソッドが追加されます。

次に、次のように使用できます。

var x = 1.2, y = -1.2;

x.integer(); // 1
y.integer(); // -1

(-10 / 3).integer(); // -3

明らかに double bitwise-not が数値をフロアする最速の方法です。

var x = 2.5;
console.log(~~x); // 2

以前はここに記事がありましたが、現在は 404 になっています。 http://james.padolsey.com/javascript/double-bitwise-not/

Googleはそれをキャッシュしています: http://74.125.155.132/search?q=cache:wpZnhsbJGt0J:james.padolsey.com/javascript/double-bitwise-not/+double+bitwise+not&cd=1&hl=en&ct=clnk&gl=us

しかし、ウェイバック マシンがその窮地を救ってくれます。 http://web.archive.org/web/20100422040551/http://james.padolsey.com/javascript/double-bitwise-not/

答えはすでに出ていますが、念のために言っておきます。

これには数学ライブラリを使用してください。ラウンド、天井、または床の機能。

parseInt は文字列を int に変換するためのものですが、ここでは必要ありません

toFixed は浮動小数点数を文字列に変換するためのものであり、ここで必要なものではありません

数学関数は文字列への、または文字列からの変換を行わないため、とにかく間違っている他の選択肢よりも高速になります。

Number(a).toFixed(0); を使用できます。

あるいは単に a.toFixed(0); だけでも構いません。

編集:

これは 0 位に丸めることであり、切り捨てとは少し異なります。また、他の人が示唆したように、toFixed は生の整数ではなく文字列を返します。ディスプレイ目的に便利です。

var num = 2.7;  // typeof num is "Number"
num.toFixed(0) == "3"

一番いい方法 依存します の上:

  • 丸めモード:何 丸めの種類 (浮動小数点から整数へ)あなたが期待/要求するもの
    小数部のある正および/または負の数値の場合。
    一般的な例:
    float | trunc | floor |  ceil | near (half up)
    ------+-------+-------+-------+---------------
    +∞    |   +∞  |   +∞  |   +∞  |   +∞  
    +2.75 |   +2  |   +2  |   +3  |   +3
    +2.5  |   +2  |   +2  |   +3  |   +3
    +2.25 |   +2  |   +2  |   +3  |   +2
    +0    |   +0  |   +0  |   +0  |   +0
     NaN  |  NaN  |  NaN  |  NaN  |  NaN
    -0    |   -0  |   -0  |   -0  |   -0
    -2.25 |   -2  |   -3  |   -2  |   -2
    -2.5  |   -2  |   -3  |   -2  |   -2
    -2.75 |   -2  |   -3  |   -2  |   -3
    -∞    |   -∞  |   -∞  |   -∞  |   -∞  
    
    浮動小数点から整数への変換では、 一般的に 期待する 「切り捨て」
    (別名 「ゼロに向かって丸める」 別名 「無限遠から遠ざかる」).
    事実上、これは浮動小数点数の小数部分を「切り取る」だけです。
    ほとんどのテクニックと (内部) 組み込みメソッドはこのように動作します。
  • 入力:(浮動小数点) 数値がどのように表現されるか:
    • String
      通常、基数/基数:10 (10進数)
    • 浮動小数点 (「内部」) Number
  • 出力:結果の値をどうしたいか:
    • (中間) 出力 String (デフォルトの基数 10) (画面上)
    • 結果の値に対してさらに計算を実行する
  • 範囲:
    どのような数値範囲の入力/計算結果が予想されますか
    また、対応する「正しい」出力が期待される範囲はどれですか。

のみ これらの考慮事項が解決されたら、適切な方法と速度を考えることができます。


ECMAScript 262 仕様によると: 全て 数字 (タイプ Number) JavaScript では、次の場所に表現/保存されます。
"IEEE 754 倍精度浮動小数点 (binary64)" フォーマット。
したがって、整数は また で表される 同じ 浮動小数点形式 (小数点のない数値として)。
注記:ほとんどの実装 する より効率的な (速度とメモリ サイズの点で) 整数型を使用する 内部的に 可能であれば!

この形式は 1 つの符号ビット、11 の指数ビット、および最初の 53 の有効ビット (「仮数」) を格納するため、次のように言えます。 のみ Number-値 -252 そして +252 分数を持つこともできます。
言い換えると: 全て 表現可能なポジティブとネガティブ Number-値 252 に(ほぼ) 2(211/2=1024) (この時点でフォーマットはそれを呼び出します 一日 Infinity) はすでに整数です (残りの小数および/または最下位整数を表すビットが残っていないため、内部で丸められます)。

そして、最初の「落とし穴」があります。
の内部丸めモードを制御することはできません。 Number- 組み込みのリテラル/文字列から浮動小数点数への変換の結果 (丸めモード:IEEE 754-2008「最も近いものに丸め、偶数に結び付ける」) および組み込みの算術演算 (rounding-mode:IEEE 754-2008「最近接への丸め」)。
例えば:
252+0.25 = 4503599627370496.25 丸められて次のように格納されます。 4503599627370496
252+0.50 = 4503599627370496.50 丸められて次のように格納されます。 4503599627370496
252+0.75 = 4503599627370496.75 丸められて次のように格納されます。 4503599627370497
252+1.25 = 4503599627370497.25 丸められて次のように格納されます。 4503599627370497
252+1.50 = 4503599627370497.50 丸められて次のように格納されます。 4503599627370498
252+1.75 = 4503599627370497.75 丸められて次のように格納されます。 4503599627370498
252+2.50 = 4503599627370498.50 丸められて次のように格納されます。 4503599627370498
252+3.50 = 4503599627370499.50 丸められて次のように格納されます。 4503599627370500

丸めを制御するには Number 小数部分 (およびそれを表す少なくとも 1 ビット) が必要です。そうでない場合、ceil/floor/trunc/near は入力された整数を返します。

有効小数桁 x 桁までの数値を正しく天井/下限/切り捨てるには、対応する小数の最小値と最大値が四捨五入後も 2 進の小数数値を与えるかどうかだけを考慮します (つまり、上限または下限にならない)。次の整数)。
したがって、たとえば、(ceil/floor/trunc の) 有効小数第 1 桁までの「正しい」丸めが期待される場合 (x.1 to x.9)、必要です 少なくとも 3 ビット (4 ビットではありません) を提供します ある 2 進の小数値:
0.1 に近いです 1/(23=8)=0.125 それよりも 0 そして 0.9 に近いです 1-1/(23=8)=0.875 それよりも 1.

のみ まで ±2(53-3=50) すべての表現可能な値は、ゼロ以外の 2 進小数を持ちます。 初め 有効小数の小数桁 (値 x.1x.9).
小数点以下2桁の場合 ±2(53-6=47), 、小数点 3 桁の場合 ±2(53-9=44), 、小数点 4 桁の場合 ±2(53-13=40), 、小数点 5 桁の場合 ±2(53-16=37), 、小数点6桁の場合 ±2(53-19=34), 、小数点 7 桁の場合 ±2(53-23=30), 、小数点 8 桁の場合 ±2(53-26=27), 、小数点9桁の場合 ±2(53-29=24), 、小数点以下 10 桁の場合 ±2(53-33=20), 、小数点 11 桁の場合 ±2(53-36=17), 、など。

「安全な整数」 JavaScript では、 は整数です。

  • それはあり得る その通り IEEE-754 倍精度数値として表され、
  • その IEEE-754 表現 できない IEEE-754 表現に適合するように他の整数を四捨五入した結果になります。
    (それでも ±253 (正確な 2 の累乗として) を正確に表すことができます。 ない 安全な整数である可能性もあるため、 ±(253+1) 最大 53 の最上位ビットに収まるように丸められる前)。

これは、(安全に表現可能な) 整数のサブセット範囲を効果的に定義します。 -253 そして +253:

  • から: -(253 - 1) = -9007199254740991 (包括的)
    (静的プロパティとして提供される定数 Number.MIN_SAFE_INTEGER ES6以降)
  • に: +(253 - 1) = +9007199254740991 (包括的)
    (静的プロパティとして提供される定数 Number.MAX_SAFE_INTEGER ES6以降)
    これら 2 つの新しい ES6 定数の簡単なポリフィル:

    Number.MIN_SAFE_INTEGER || (Number.MIN_SAFE_INTEGER=
      -(Number.MAX_SAFE_INTEGER=9007199254740991) //Math.pow(2,53)-1
    );
    


ES6 以降、補完的な静的メソッドもあります Number.isSafeInteger() 渡された値が次の型であるかどうかをテストします。 Number 安全な整数範囲内の整数です (ブール値を返します) true または false).
注記:また戻ります false のために: NaN, Infinity そして明らかに String (数値を表す場合でも)。
ポリフィル :

Number.isSafeInteger || (Number.isSafeInteger = function(value){
  return typeof value === 'number' && 
                value === Math.floor(value) &&
                value  <   9007199254740992 &&
                value  >  -9007199254740992;
});

ECMAScript 2015 / ES6 は新しい静的メソッドを提供します Math.trunc()
浮動小数点を整数に切り捨てるには:

小数部を削除して、数値 x の整数部分を返します。x がすでに整数の場合、結果は x になります。

あるいは、もっと簡単に言うと (MDN):

他の 3 つの数学メソッドとは異なります。 Math.floor(), Math.ceil() そして Math.round(), 、 道 Math.trunc() 動作は非常にシンプルで簡単です。
引数が正の数であるか負の数であるかに関係なく、ドットとその後ろの数字を切り捨てるだけです。

(およびポリフィルについて) さらに詳しく説明します。 Math.trunc() そのような:

Math.trunc || (Math.trunc = function(n){
    return n < 0 ? Math.ceil(n) : Math.floor(n); 
});

上記のポリフィルのペイロードは、 潜在的に 以下に比べて、エンジンによる事前最適化がより優れています。
Math[n < 0 ? 'ceil' : 'floor'](n);

使用法: Math.trunc(/* Number or String */)
入力:(整数または浮動小数点) Number (ただし、喜んで文字列を数値に変換しようとします)
出力:(整数) Number (ただし、文字列コンテキストで数値を文字列に喜んで変換しようとします)
範囲: -2^52+2^52 (これを超えると、単純に「丸め誤差」(およびある時点で科学的/指数表記) が発生することを予期する必要があります。 Number IEEE 754 の入力はすでに小数精度を失っています。間の数値以降 ±2^52±2^53 すでに 内部が丸い 整数 (たとえば、 4503599627370509.5 内部的にはすでに次のように表されています 4503599627370510) 以降 ±2^53 整数も精度 (2 の累乗) を失います。


減算による浮動小数点から整数への変換 残り (%)による除算の 1:

例: result = n-n%1 (または n-=n%1)
これも 切り詰める 浮く。Remainder 演算子の値が大きいため、 優先順位 減算よりも効果的に次のことが得られます。 (n)-(n%1).
正の数値の場合、これが値を下限にすることは簡単にわかります。 (2.5) - (0.5) = 2,
負の数値の場合、これが値の上限となります。 (-2.5) - (-0.5) = -2 (なぜなら --=+ それで (-2.5) + (0.5) = -2).

以来、 入力 そして 出力Number 私たちは すべき を入手する 同じ有効範囲と出力 ES6と比較して Math.trunc() (またはポリフィルです)。
注記:厳しい私 恐れ (確信はありません) 違いがあるかもしれません:元の Number (浮動小数点数) と 2 番目に派生した Number (分数) に対して算術演算 (内部で丸めモード「nearTiesEven」 (別名 Banker's Rounding) を使用) を実行しているため、digital_representation と算術丸めエラーの複合を招く可能性があるため、結局floatを返します..


(ab-) を使用した浮動小数点から整数への変換 ビット単位の演算:

これは次のように動作します。 内部的に (浮動小数点) を強制する Number 符号付き 32 ビット整数値 (2 の補数) への変換 (切り捨てとオーバーフロー) ビット単位の演算を使用するNumber (そして結果は (浮動小数点) に変換されます。 Number これは整数値のみを保持します)。

また、 入力 そして 出力Number (そして再び、文字列入力から数値へ、および数値出力から文字列へのサイレント変換)。

さらに重要な問題 (そして通常は忘れられ、説明されません):
ビット単位の演算と数値の符号に応じて, 、 役に立つ 範囲 になるだろう 限定 間:
-2^31+2^31 (のように ~~num または num|0 または num>>0) または 0+2^32 (num>>>0).

これは、次のルックアップ テーブル (次の内容を含む) によってさらに明確になります。 全て 「重大な」例):

              n             | n>>0 OR n<<0 OR   |    n>>>0    | n < 0 ? -(-n>>>0) : n>>>0
                            | n|0 OR n^0 OR ~~n |             |
                            | OR n&0xffffffff   |             |
----------------------------+-------------------+-------------+---------------------------
+4294967298.5 = (+2^32)+2.5 |             +2    |          +2 |          +2
+4294967297.5 = (+2^32)+1.5 |             +1    |          +1 |          +1
+4294967296.5 = (+2^32)+0.5 |              0    |           0 |           0
+4294967296   = (+2^32)     |              0    |           0 |           0
+4294967295.5 = (+2^32)-0.5 |             -1    | +4294967295 | +4294967295
+4294967294.5 = (+2^32)-1.5 |             -2    | +4294967294 | +4294967294
       etc...               |         etc...    |      etc... |      etc...
+2147483649.5 = (+2^31)+1.5 |    -2147483647    | +2147483649 | +2147483649
+2147483648.5 = (+2^31)+0.5 |    -2147483648    | +2147483648 | +2147483648
+2147483648   = (+2^31)     |    -2147483648    | +2147483648 | +2147483648
+2147483647.5 = (+2^31)-0.5 |    +2147483647    | +2147483647 | +2147483647
+2147483646.5 = (+2^31)-1.5 |    +2147483646    | +2147483646 | +2147483646
       etc...               |         etc...    |      etc... |      etc...
         +1.5               |             +1    |          +1 |          +1
         +0.5               |              0    |           0 |           0
          0                 |              0    |           0 |           0
         -0.5               |              0    |           0 |           0
         -1.5               |             -1    | +4294967295 |          -1
       etc...               |         etc...    |      etc... |      etc...
-2147483646.5 = (-2^31)+1.5 |    -2147483646    | +2147483650 | -2147483646
-2147483647.5 = (-2^31)+0.5 |    -2147483647    | +2147483649 | -2147483647
-2147483648   = (-2^31)     |    -2147483648    | +2147483648 | -2147483648
-2147483648.5 = (-2^31)-0.5 |    -2147483648    | +2147483648 | -2147483648
-2147483649.5 = (-2^31)-1.5 |    +2147483647    | +2147483647 | -2147483649
-2147483650.5 = (-2^31)-2.5 |    +2147483646    | +2147483646 | -2147483650
       etc...               |         etc...    |      etc... |      etc...
-4294967294.5 = (-2^32)+1.5 |             +2    |          +2 | -4294967294
-4294967295.5 = (-2^32)+0.5 |             +1    |          +1 | -4294967295
-4294967296   = (-2^32)     |              0    |           0 |           0
-4294967296.5 = (-2^32)-0.5 |              0    |           0 |           0
-4294967297.5 = (-2^32)-1.5 |             -1    | +4294967295 |          -1
-4294967298.5 = (-2^32)-2.5 |             -2    | +4294967294 |          -2

注1:最後の列の範囲が拡張されました 0-4294967295 を使用して (n < 0 ? -(-n>>>0) : n>>>0).
注2:ビットごとに独自の変換オーバーヘッドが発生します(s) (重大度と Math 実際の実装に依存するため、ビット単位で できた より高速になります(多くの場合、古い歴史的なブラウザで)。


明らかに、「浮動小数点数」が String まず、
parseInt(/*String*/, /*Radix*/) 整数に解析するのが適切な選択です Number.
parseInt() 意思 切り詰める 同様に (正の数と負の数の場合)。
範囲 上で説明したように、やはり IEEE 754 倍精度浮動小数点に制限されます。 Math メソッド。

最後に、もしあなたが持っているなら、 String そして期待します String 出力として、基数点と小数点を切り取ることもできます (これにより、IEEE 754 倍精度浮動小数点と比較して、より正確な切り捨て範囲が得られます (±2^52))!


余分な:
上記の情報から、知っておくべきことはすべて得られたはずです。

たとえば、あなたが望むなら ゼロから遠ざける (別名 無限に向かって丸くする) を変更できます Math.trunc() ポリフィル、用 :

Math.intToInf || (Math.intToInf = function(n){
    return n < 0 ? Math.floor(n) : Math.ceil(n); 
});
var i = parseInt(n, 10);

次のように基数値を指定しない場合 '010' は 8 進数として扱われます (結果は次のようになります) 8 ない 10).

ビット単位の演算子の使用。これは整数に変換する最も明確な方法ではないかもしれませんが、どのような種類のデータ型でも機能します。

関数が引数を取るとします value, 、関数は次のように動作します。 value 常に整数である必要があります (0 も受け入れられます)。次に、次のいずれかが割り当てられます value 整数として:

value = ~~(value)
value = value | 0;
value = value & 0xFF;   // one byte; use this if you want to limit the integer to
                        // a predefined number of bits/bytes

最も良い点は、これが数値である文字列 (テキスト入力などから取得できるもの) を処理できることです。 ~~("123.45") === 123. 。数値以外の値は次のようになります。 0, 、つまり、

~~(undefined) === 0
~~(NaN) === 0
~~("ABC") === 0

16 進数を文字列として扱うことはできます ( 0x プレフィックス)

~~("0xAF") === 175

何らかの型強制が関係していると思います。これらを比較するためにいくつかのパフォーマンス テストを実行します。 parseInt() そして Math.floor(), 、しかし、私は余分な利便性がないのが好きです。 Errors 投げられて、 0 数字以外の場合

そこでベンチマークを作成しました。 Chrome 入力がすでに数値の場合、最も速いのは ~~num そして num|0, 、半分の速度: Math.floor, 、そして最も遅いのは parseInt 見る ここ

benchmark result

編集:すでに他にも作った人がいるようです 丸める ベンチマーク (詳細な結果) および追加 方法: num>>0 (できるだけ速く |0) そして num - num%1 (速い場合もあります)

この質問は、特に float から int への変換について尋ねているようです。私の理解では、これを行う方法は次のとおりです toFixed. 。それで...

var myFloat = 2.5;
var myInt = myFloat.toFixed(0);

誰か知っていますか? Math.floor() 多かれ少なかれ以下よりもパフォーマンスが高い Number.toFixed()?

このようにすることもできます:

var string = '1';
var integer = a * 1;

parseInt() がおそらく最良のものです。 a | 0 本当にやりたいことはしません (a が未定義または null 値の場合に 0 を割り当てるだけです。つまり、空のオブジェクトまたは配列がテストに合格することを意味します)。Math.floor は何らかの型のトリックによって機能します (基本的に parseInt() を呼び出します)。背景にあります)。

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