現代のJavaScriptには、2種類の数値型があります。
-
JavaScriptの通常の数値は、64ビット形式のIEEE-754(「倍精度浮動小数点数」としても知られています)で格納されます。これらは私たちがほとんどの場合使用する数値であり、この章でそれらについて説明します。
-
BigInt数値は、任意の長さの整数を表します。通常の整数は、
(253-1)
を超えたり、-(253-1)
を下回ったりすると安全ではなくなるため、必要な場合があります。これは、前の章データ型で説明しました。BigIntはいくつかの特別な分野で使用されるため、特別な章BigIntでそれらについて説明します。
ここでは、通常の数値について説明します。それらについての知識を広げましょう。
数値の書き方の別の方法
10億を書きたいとしましょう。明らかな方法は
let billion = 1000000000;
アンダースコア _
を区切り文字として使用することもできます
let billion = 1_000_000_000;
ここでアンダースコア _
は「糖衣構文」の役割を果たし、数値をより読みやすくします。JavaScriptエンジンは数字の間の _
を単純に無視するため、上記の10億と全く同じです。
しかし実際には、長いゼロの列を書くことは避けようとします。面倒だからです。10億に対して "1bn"
とか、73億に対して "7.3bn"
のように書きたいと思うでしょう。ほとんどの大きな数値についても同様です。
JavaScriptでは、数値に文字 "e"
を追加してゼロの数を指定することで、数値を短縮することができます
let billion = 1e9; // 1 billion, literally: 1 and 9 zeroes
alert( 7.3e9 ); // 7.3 billions (same as 7300000000 or 7_300_000_000)
言い換えれば、e
は、数値に与えられたゼロの数で 1
を掛けます。
1e3 === 1 * 1000; // e3 means *1000
1.23e6 === 1.23 * 1000000; // e6 means *1000000
次に、非常に小さな数を書いてみましょう。例えば、1マイクロ秒(100万分の1秒)
let mсs = 0.000001;
以前と同様に、"e"
を使用すると便利です。ゼロを明示的に書くことを避けたい場合は、次のように書くことができます。
let mcs = 1e-6; // five zeroes to the left from 1
0.000001
のゼロの数を数えると、6つあります。したがって、当然 1e-6
となります。
言い換えれば、"e"
の後の負の数は、与えられた数のゼロを持つ1による除算を意味します
// -3 divides by 1 with 3 zeroes
1e-3 === 1 / 1000; // 0.001
// -6 divides by 1 with 6 zeroes
1.23e-6 === 1.23 / 1000000; // 0.00000123
// an example with a bigger number
1234e-2 === 1234 / 100; // 12.34, decimal point moves 2 times
16進数、2進数、8進数
16進数は、色を表したり、文字をエンコードしたり、その他多くのことにJavaScriptで広く使用されています。そのため、当然、それらを記述するより短い方法があります: 0x
とその後の数値です。
例えば
alert( 0xff ); // 255
alert( 0xFF ); // 255 (the same, case doesn't matter)
2進数と8進数の数値システムはめったに使用されませんが、0b
および 0o
のプレフィックスを使用してサポートされています
let a = 0b11111111; // binary form of 255
let b = 0o377; // octal form of 255
alert( a == b ); // true, the same number 255 at both sides
このようなサポートがあるのは3つの数値システムだけです。他の数値システムについては、関数 parseInt
を使用する必要があります(これについては、この章で後ほど説明します)。
toString(base)
メソッド num.toString(base)
は、指定された base
の数値システムでの num
の文字列表現を返します。
例えば
let num = 255;
alert( num.toString(16) ); // ff
alert( num.toString(2) ); // 11111111
base
は 2
から 36
の範囲で変動する可能性があります。デフォルトでは、10
です。
一般的なユースケースは
-
base=16 は、16進数の色、文字エンコーディングなどに使用され、数字は
0..9
またはA..F
になります。 -
base=2 は、主にビット単位の操作をデバッグするために使用され、数字は
0
または1
になります。 -
base=36 は最大で、数字は
0..9
またはA..Z
になります。ラテンアルファベット全体が数値を表すために使用されます。36
の面白いですが便利なケースは、長い数値識別子を、たとえば、短いURLを作成するなど、より短いものに変換する必要がある場合です。base36
の数値システムで簡単に表すことができますalert( 123456..toString(36) ); // 2n9c
123456..toString(36)
の2つのドットはタイプミスではないことに注意してください。上記の例の toString
のように、数値上で直接メソッドを呼び出す場合は、その後に2つのドット ..
を配置する必要があります。
1つのドットを配置した場合:123456.toString(36)
、最初のドットの後に小数点部分が続くため、エラーが発生します。もう1つのドットを配置すると、JavaScriptは小数点部分が空であり、メソッドに進むことを認識します。
また、(123456).toString(36)
と書くこともできます。
丸め
数値を使用する場合に最も使用される操作の1つは丸めです。
丸めにはいくつかの組み込み関数があります
Math.floor
- 切り捨て:
3.1
は3
になり、-1.1
は-2
になります。 Math.ceil
- 切り上げ:
3.1
は4
になり、-1.1
は-1
になります。 Math.round
- 最も近い整数に丸めます:
3.1
は3
になり、3.6
は4
になり、中間の場合:3.5
も4
に丸められます。 Math.trunc
(Internet Explorerではサポートされていません)- 丸めを行わずに小数点以下のすべてを削除します:
3.1
は3
になり、-1.1
は-1
になります。
これらの違いをまとめた表を以下に示します
Math.floor |
Math.ceil |
Math.round |
Math.trunc |
|
---|---|---|---|---|
3.1 |
3 |
4 |
3 |
3 |
3.6 |
3 |
4 |
4 |
3 |
-1.1 |
-2 |
-1 |
-1 |
-1 |
-1.6 |
-2 |
-1 |
-2 |
-1 |
これらの関数は、数値の小数点部分を処理するための可能なすべての方法を網羅しています。しかし、小数点以下のn
桁に数値を丸めたい場合はどうでしょうか?
たとえば、1.2345
があり、それを2桁に丸めて 1.23
だけを取得したいとします。
これを行うには2つの方法があります
-
掛け算と割り算。
たとえば、数値を小数点以下の2桁に丸めるには、数値を
100
で掛け、丸め関数を呼び出してから、それを割り戻します。let num = 1.23456; alert( Math.round(num * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23
-
メソッドtoFixed(n)は、数値を小数点以下の
n
桁に丸め、結果の文字列表現を返します。let num = 12.34; alert( num.toFixed(1) ); // "12.3"
これは、
Math.round
と同様に、最も近い値に切り上げまたは切り下げを行いますlet num = 12.36; alert( num.toFixed(1) ); // "12.4"
toFixed
の結果は文字列であることに注意してください。小数点以下の部分が必要なものより短い場合は、最後にゼロが追加されますlet num = 12.34; alert( num.toFixed(5) ); // "12.34000", added zeroes to make exactly 5 digits
単項プラスまたは
Number()
呼び出しを使用して数値に変換できます。たとえば、+num.toFixed(5)
と記述します。
不正確な計算
内部的には、数値は64ビット形式のIEEE-754で表されるため、数値を格納するために正確に64ビットあります。そのうち52ビットは数字の格納に使用され、11ビットは小数点位置の格納に使用され、1ビットは符号に使用されます。
数値が非常に大きい場合、64ビットストレージがオーバーフローし、特殊な数値である Infinity
になる可能性があります
alert( 1e500 ); // Infinity
少し明白ではないかもしれませんが、非常に頻繁に発生するのは、精度の損失です。
この(誤った!)等価性テストを考えてみましょう
alert( 0.1 + 0.2 == 0.3 ); // false
そうです、0.1
と 0.2
の合計が 0.3
であるかどうかを確認すると、false
が返されます。
おかしい!0.3
でない場合は何なのでしょうか?
alert( 0.1 + 0.2 ); // 0.30000000000000004
うわ!あなたがeショップサイトを作成していて、訪問者がカートに $0.10
と $0.20
の商品を入れたと想像してみてください。注文の合計は $0.30000000000000004
になります。それは誰にとっても驚きでしょう。
しかし、なぜこうなるのでしょうか?
数値は、1と0のビットのシーケンスであるバイナリ形式でメモリに格納されます。しかし、10進数のシステムでは単純に見える 0.1
や 0.2
のような分数は、実際にはバイナリ形式では終わりのない分数です。
alert(0.1.toString(2)); // 0.0001100110011001100110011001100110011001100110011001101
alert(0.2.toString(2)); // 0.001100110011001100110011001100110011001100110011001101
alert((0.1 + 0.2).toString(2)); // 0.0100110011001100110011001100110011001100110011001101
0.1
とは何でしょうか?それは10分の1 1/10
、つまり10分の1です。10進数の数値システムでは、このような数値は簡単に表現できます。それを3分の1: 1/3
と比較してください。それは無限小数 0.33333(3)
になります。
したがって、10
のべき乗による除算は10進数システムではうまく機能することが保証されていますが、3
による除算はそうではありません。同様の理由で、2進数の数値システムでは、2
のべき乗による除算はうまく機能することが保証されていますが、1/10
は無限の2進数になります。
10進数として3分の1を格納する方法がないのと同じように、2進数システムを使用して正確に0.1または正確に0.2を格納する方法はありません。
数値形式IEEE-754は、最も近い可能な数値に丸めることでこれを解決します。これらの丸めルールにより、通常は「わずかな精度の損失」を見ることができませんが、それは存在します。
これを実際に見てみましょう
alert( 0.1.toFixed(20) ); // 0.10000000000000000555
そして、2つの数値を合計すると、それらの「精度の損失」が加算されます。
それが、0.1 + 0.2
が正確に 0.3
にならない理由です。
同じ問題が他の多くのプログラミング言語にも存在します。
PHP、Java、C、Perl、およびRubyは、同じ数値形式に基づいているため、全く同じ結果が得られます。
この問題を回避できますか?もちろんです。最も信頼できる方法は、メソッド toFixed(n) を使用して結果を丸めることです
let sum = 0.1 + 0.2;
alert( sum.toFixed(2) ); // "0.30"
toFixed
は常に文字列を返すことに注意してください。小数点以下2桁になるように保証します。これは、オンラインショッピングで $0.30
のように表示する必要がある場合に非常に便利です。それ以外の場合は、単項プラスを使って数値に強制変換できます。
let sum = 0.1 + 0.2;
alert( +sum.toFixed(2) ); // 0.3
また、一時的に数値を100倍(またはそれ以上の数)にして整数にし、計算を行い、その後で元の数値に戻すこともできます。整数で計算を行うことで、誤差はある程度小さくなりますが、除算の際に誤差が発生します。
alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3
alert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001
したがって、掛け算/割り算のアプローチは誤差を減らしますが、完全には取り除くことはできません。
場合によっては、分数自体を避けることもできます。たとえば、ショップを扱っている場合、価格をドルではなくセント単位で保存できます。しかし、30%の割引を適用する場合はどうでしょうか?実際には、分数を完全に避けることはめったに不可能です。必要な場合は、丸めて「尻尾」を切り落とすだけです。
これを実行してみてください。
// Hello! I'm a self-increasing number!
alert( 9999999999999999 ); // shows 10000000000000000
これも同じ問題(精度の低下)の影響を受けています。数値には64ビットが割り当てられており、そのうち52ビットが数字の格納に使用できますが、それでは十分ではありません。そのため、最下位の桁が失われます。
JavaScriptはこのような場合にエラーを発生させません。数値を希望の形式に合わせようと最善を尽くしますが、残念ながら、この形式は十分な大きさではありません。
数値の内部表現のもう1つの面白い結果は、2つのゼロ、0
と -0
が存在することです。
これは、符号が単一のビットで表現されるため、ゼロを含む任意の数値に対して設定または設定解除できるためです。
ほとんどの場合、演算子は同じものとして扱うように設計されているため、この区別は目立ちません。
テスト:isFinite と isNaN
これらの2つの特別な数値の値覚えていますか?
Infinity
(および-Infinity
)は、どんな数値よりも大きい(小さい)特別な数値です。NaN
はエラーを表します。
これらは number
型に属しますが、「通常の」数値ではないため、それらをチェックするための特別な関数があります。
-
isNaN(value)
は、引数を数値に変換してから、NaN
であるかどうかをテストします。alert( isNaN(NaN) ); // true alert( isNaN("str") ); // true
しかし、この関数は必要でしょうか?
=== NaN
の比較を使用することはできませんか?残念ながら、できません。値NaN
は、それ自体を含む何とも等しくないという点で独特です。alert( NaN === NaN ); // false
-
isFinite(value)
は、引数を数値に変換し、NaN/Infinity/-Infinity
でない通常の数値である場合にtrue
を返します。alert( isFinite("15") ); // true alert( isFinite("str") ); // false, because a special value: NaN alert( isFinite(Infinity) ); // false, because a special value: Infinity
isFinite
は、文字列の値が通常の数値であるかどうかを検証するために使用されることがあります。
let num = +prompt("Enter a number", '');
// will be true unless you enter Infinity, -Infinity or not a number
alert( isFinite(num) );
空またはスペースのみの文字列は、isFinite
を含むすべての数値関数で 0
として扱われることに注意してください。
Number.isNaN
と Number.isFinite
Number.isNaN と Number.isFinite メソッドは、isNaN
および isFinite
関数のより「厳密」なバージョンです。これらは引数を数値に自動変換するのではなく、number
型に属するかどうかをチェックします。
-
Number.isNaN(value)
は、引数がnumber
型に属し、かつNaN
である場合にtrue
を返します。それ以外の場合はfalse
を返します。alert( Number.isNaN(NaN) ); // true alert( Number.isNaN("str" / 2) ); // true // Note the difference: alert( Number.isNaN("str") ); // false, because "str" belongs to the string type, not the number type alert( isNaN("str") ); // true, because isNaN converts string "str" into a number and gets NaN as a result of this conversion
-
Number.isFinite(value)
は、引数がnumber
型に属し、かつNaN/Infinity/-Infinity
でない場合にtrue
を返します。それ以外の場合はfalse
を返します。alert( Number.isFinite(123) ); // true alert( Number.isFinite(Infinity) ); // false alert( Number.isFinite(2 / 0) ); // false // Note the difference: alert( Number.isFinite("123") ); // false, because "123" belongs to the string type, not the number type alert( isFinite("123") ); // true, because isFinite converts string "123" into a number 123
ある意味では、Number.isNaN
と Number.isFinite
は、isNaN
および isFinite
関数よりも単純でわかりやすいです。実際には、isNaN
と isFinite
の方が記述が短いため、ほとんど使用されます。
Object.is
との比較===
のように値を比較する特別な組み込みメソッド Object.is
がありますが、2つのエッジケースでより信頼性が高くなります。
NaN
で動作します。Object.is(NaN, NaN) === true
であり、良いことです。- 値
0
と-0
は異なります。Object.is(0, -0) === false
です。技術的には、数値には、他のすべてのビットがゼロであっても異なる可能性がある符号ビットがあるため、これは正しいです。
それ以外の場合、Object.is(a, b)
は a === b
と同じです。
Object.is
は、JavaScript の仕様でよく使用されるため、ここで言及します。内部アルゴリズムで2つの値を完全に同じかどうか比較する必要がある場合、Object.is
(内部的には SameValue と呼ばれます)を使用します。
parseInt と parseFloat
プラス +
または Number()
を使用した数値変換は厳密です。値が正確な数値でない場合、失敗します。
alert( +"100px" ); // NaN
唯一の例外は、文字列の先頭または末尾のスペースで、これらは無視されます。
しかし、現実の世界では、CSS の "100px"
や "12pt"
のように単位付きの値を持つことがよくあります。また、多くの国では、通貨記号が金額の後ろに来るため、"19€"
のように数値を取り出したい場合もあります。
これが、parseInt
と parseFloat
がある理由です。
これらは、それ以上読み取れなくなるまで文字列から数値を「読み取り」ます。エラーが発生した場合、収集された数値が返されます。関数 parseInt
は整数を返し、parseFloat
は浮動小数点数を返します。
alert( parseInt('100px') ); // 100
alert( parseFloat('12.5em') ); // 12.5
alert( parseInt('12.3') ); // 12, only the integer part is returned
alert( parseFloat('12.3.4') ); // 12.3, the second point stops the reading
parseInt/parseFloat
が NaN
を返す状況があります。これは、数字が読み取れなかった場合に発生します。
alert( parseInt('a123') ); // NaN, the first symbol stops the process
parseInt(str, radix)
の2番目の引数parseInt()
関数には、オプションの2番目のパラメータがあります。これは、数値システムの基数を指定するため、parseInt
は16進数、2進数などの文字列を解析することもできます。
alert( parseInt('0xff', 16) ); // 255
alert( parseInt('ff', 16) ); // 255, without 0x also works
alert( parseInt('2n9c', 36) ); // 123456
その他の数学関数
JavaScript には、数学関数と定数の小さなライブラリを含む組み込みの Math オブジェクトがあります。
いくつかの例
Math.random()
-
0から1(1は含まない)の間の乱数を返します。
alert( Math.random() ); // 0.1234567894322 alert( Math.random() ); // 0.5435252343232 alert( Math.random() ); // ... (any random numbers)
Math.max(a, b, c...)
とMath.min(a, b, c...)
-
任意の数の引数の中から最大値と最小値を返します。
alert( Math.max(3, 5, -10, 0, 1) ); // 5 alert( Math.min(1, 2) ); // 1
Math.pow(n, power)
-
n
を指定されたべき乗で累乗した値を返します。alert( Math.pow(2, 10) ); // 2 in power 10 = 1024
Math
オブジェクトには、三角関数を含むより多くの関数と定数があります。これについては、Math オブジェクトのドキュメントを参照してください。
まとめ
ゼロをたくさん持つ数値を記述するには
- 数値にゼロの数を付けて
"e"
を追加します。例:123e6
は、6つのゼロが付いた123
の123000000
と同じです。 "e"
の後の負の数は、その数値を与えられたゼロの数で1で割ることを意味します。例:123e-6
は0.000123
(123ミリオン分の1)を意味します。
異なる数値システムの場合
- 16進数(
0x
)、8進数(0o
)、および2進数(0b
)システムで直接数値を記述できます。 parseInt(str, base)
は、文字列str
を与えられたbase
の数値システムで整数に解析します。2 ≤ base ≤ 36
です。num.toString(base)
は、数値を指定されたbase
の数値システムで文字列に変換します。
通常の数値テストの場合
isNaN(value)
は、引数を数値に変換してから、NaN
であるかどうかをテストします。Number.isNaN(value)
は、引数がnumber
型に属するかどうかを確認し、もしそうであれば、NaN
であるかどうかをテストします。isFinite(value)
は、引数を数値に変換し、NaN/Infinity/-Infinity
でないかどうかをテストします。Number.isFinite(value)
は、引数がnumber
型に属するかどうかを確認し、もしそうであれば、NaN/Infinity/-Infinity
でないかどうかをテストします。
12pt
や 100px
のような値を数値に変換する場合
- 文字列から数値を読み取り、エラーの前に読み取れた値を返す「ソフト」変換には、
parseInt/parseFloat
を使用します。
分数の場合
Math.floor
、Math.ceil
、Math.trunc
、Math.round
、またはnum.toFixed(precision)
を使用して丸めます。- 分数で作業するときには、精度が失われることを必ず覚えておいてください。
その他の数学関数
- 必要な場合は、Math オブジェクトを参照してください。ライブラリは非常に小さいですが、基本的なニーズをカバーできます。
コメント
<code>
タグを使用し、複数行の場合は<pre>
タグで囲み、10行以上場合はサンドボックス (plnkr, jsbin, codepen…) を使用してください。