最新の JavaScript には、次の 2 種類の数値があります。
JavaScript の通常の数値は、「倍精度浮動小数点数」とも呼ばれる 64 ビット形式の IEEE-754 で保存されます。これらは私たちがほとんどの場合使用する数値であり、この章で説明します。
BigInt 数値は、任意の長さの整数を表します。 「データ型」の章で前述したように、通常の整数値は(2 53 -1)
を安全に超えたり、 -(2 53 -1)
未満にすることができないため、これらが必要になる場合があります。 bigint はいくつかの特別な領域で使用されるため、BigInt の特別な章に割り当てます。
そこで、ここでは通常の数値について説明します。それらについての知識を広げてみましょう。
10 億を書き込む必要があると想像してください。明らかな方法は次のとおりです。
10 億 = 1000000000 とします。
区切り文字としてアンダースコア_
使用することもできます。
10億 = 1_000_000_000;
ここで、アンダースコア_
「糖衣構文」の役割を果たし、数値を読みやすくします。 JavaScript エンジンは桁間の_
無視するだけなので、上記の 10 億とまったく同じになります。
ただし、実際の生活では、長いゼロの連続を記述することは避けようとします。それには私たちは怠け者です。 10 億の場合は"1bn"
、70 億 3 億の場合は"7.3bn"
のようなものを書いてみます。ほとんどの大きな数についても同じことが当てはまります。
JavaScript では、数値に文字"e"
を追加し、ゼロの数を指定することで数値を短縮できます。
10 億 = 1e9 とします。 // 10 億、文字通り: 1 と 9 のゼロ アラート( 7.3e9 ); // 73 億 (7300000000 または 7_300_000_000 と同じ)
言い換えると、 e
指定されたゼロの数で数値に1
を掛けます。
1e3 === 1 * 1000; // e3 は *1000 を意味します 1.23e6 === 1.23 * 1000000; // e6 は *1000000 を意味します
では、非常に小さなことを書いてみましょう。たとえば、1 マイクロ秒 (100 万分の 1 秒):
mсs = 0.000001 とします。
前と同様に、 "e"
使用すると便利です。ゼロを明示的に書くことを避けたい場合は、次のように同じように書くことができます。
mcs = 1e-6 とします。 // 1 から左に 5 つのゼロ
0.000001
内のゼロを数えると、6 つあります。したがって、当然のことながら1e-6
になります。
つまり、 "e"
の後の負の数は、指定された数のゼロによる 1 による除算を意味します。
// -3 は 3 つのゼロを含む 1 で除算します 1e-3 === 1 / 1000; // 0.001 // -6 は 6 つのゼロを含む 1 で除算します。 1.23e-6 === 1.23 / 1000000; // 0.00000123 // より大きな数値の例 1234e-2 === 1234 / 100; // 12.34、小数点が2回移動します
16 進数は、色の表現、文字のエンコード、その他多くの目的で JavaScript で広く使用されています。したがって、当然のことながら、 0x
の後に数値を記述するという、より短い方法が存在します。
例えば:
アラート( 0xff ); // 255 アラート( 0xFF ); // 255 (同じ、大文字と小文字は関係ありません)
2 進数および 8 進数の表記法はほとんど使用されませんが、接頭辞0b
および0o
の使用もサポートされています。
a = 0b11111111 とします。 // 255 のバイナリ形式 b = 0o377 とします。 // 255 の 8 進数形式 アラート( a == b ); // true、両側で同じ数値 255
このようなサポートを備えた数値体系は 3 つだけです。他の数値体系の場合は、関数parseInt
使用する必要があります (この章で後ほど説明します)。
メソッドnum.toString(base)
指定されたbase
を使用した記数法でのnum
の文字列表現を返します。
例えば:
数値 = 255 とします。 アラート( num.toString(16) ); // ふふ アラート( 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 を作成するために、長い数値識別子を短いものに変換する必要がある場合です。単純に基数36
の記数法で表すことができます。
アラート( 123456..toString(36) ); // 2n9c
メソッドを呼び出すための 2 つのドット
123456..toString(36)
の 2 つのドットはタイプミスではないことに注意してください。上の例のtoString
のように、数値に対してメソッドを直接呼び出したい場合は、その後ろに 2 つのドット..
を置く必要があります。
123456.toString(36)
という単一のドットを配置すると、JavaScript 構文では最初のドットの後に小数部分が暗黙的に含まれるため、エラーが発生します。さらにドットを 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
に切り上げられ、 -3.5
-3
に切り上げられます。
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.5 | 3 | 4 | 4 | 3 |
3.6 | 3 | 4 | 4 | 3 |
-1.1 | -2 | -1 | -1 | -1 |
-1.5 | -2 | -1 | -1 | -1 |
-1.6 | -2 | -1 | -2 | -1 |
これらの関数は、数値の小数部分を処理する可能なすべての方法をカバーします。しかし、数値を小数点以下n-th
桁目に四捨五入したい場合はどうすればよいでしょうか?
たとえば、 1.2345
があり、これを 2 桁に丸めて、 1.23
のみを取得したいとします。
それには次の 2 つの方法があります。
乗算と除算。
たとえば、数値を小数点以下 2 桁に四捨五入するには、数値を100
で乗算し、丸め関数を呼び出してから、それを割り直すことができます。
数値 = 1.23456 とします。 alert( Math.round(num * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23
toFixed(n) メソッドは、小数点以下の数値をn
桁に丸め、結果の文字列表現を返します。
数値 = 12.34 とします。 アラート( num.toFixed(1) ); // "12.3"
これは、 Math.round
と同様に、最も近い値に切り上げまたは切り下げます。
数値 = 12.36 とします。 アラート( num.toFixed(1) ); // "12.4"
toFixed
の結果は文字列であることに注意してください。小数部分が必要な長さよりも短い場合は、末尾にゼロが追加されます。
数値 = 12.34 とします。 アラート( num.toFixed(5) ); // "12.34000"、ゼロを追加して正確に 5 桁にします
単項プラスまたはNumber()
呼び出しを使用して数値に変換できます (例: +num.toFixed(5)
と書き込みます)。
内部的には、数値は 64 ビット形式の IEEE-754 で表されるため、数値を保存するのにちょうど 64 ビットがあります。そのうちの 52 ビットは数字の保存に使用され、そのうちの 11 ビットは小数点の位置を保存し、1 ビットは小数点の位置を保存するために使用されます。サイン用です。
数値が非常に大きい場合、64 ビット ストレージをオーバーフローして、特殊な数値Infinity
になる可能性があります。
アラート( 1e500 ); // 無限大
少しわかりにくいかもしれませんが、非常に頻繁に発生するのは、精度の低下です。
次の (誤りです!) 等価性テストを考えてみましょう。
アラート( 0.1 + 0.2 == 0.3 ); // 間違い
そうです、 0.1
と0.2
の合計が0.3
であるかどうかをチェックすると、 false
返されます。
奇妙な!では、 0.3
ではない場合は何でしょうか?
アラート( 0.1 + 0.2 ); // 0.30000000000000004
ああ!あなたが電子ショッピング サイトを作成していて、訪問者が$0.10
と$0.20
商品をカートに入れると想像してください。注文の合計は$0.30000000000000004
になります。それは誰でも驚くでしょう。
しかし、なぜこのようなことが起こるのでしょうか?
数値は、1 と 0 のビットのシーケンスであるバイナリ形式でメモリに保存されます。しかし、 0.1
、 0.2
などの 10 進数体系では単純に見える分数は、実際には 2 進数形式では終わりのない分数です。
アラート(0.1.toString(2)); // 0.0001100110011001100110011001100110011001100110011001101 アラート(0.2.toString(2)); // 0.001100110011001100110011001100110011001100110011001101 アラート((0.1 + 0.2).toString(2)); // 0.0100110011001100110011001100110011001100110011001101
0.1
とは何ですか?それは 1 を 10 で割った1/10
、つまり 10 分の 1 です。 10 進数システムでは、このような数値は簡単に表現できます。これを 3 分の 1 と比較してください: 1/3
。無限小数0.33333(3)
になります。
したがって、 10
の累乗による除算は 10 進法で適切に機能することが保証されていますが、 3
による除算はそうではありません。同じ理由で、2 進数体系では2
のべき乗による除算は確実に機能しますが、 1/10
無限の 2 進分数になります。
3 分の 1 を小数として保存する方法がないのと同様に、2 進法を使用して正確に 0.1または正確に 0.2 を保存する方法はありません。
数値形式 IEEE-754 は、可能な限り最も近い数値に四捨五入することでこの問題を解決します。これらの丸めルールでは通常、その「わずかな精度の損失」を確認することはできませんが、実際には存在します。
これが実際に動作しているのを確認できます。
アラート( 0.1.toFixed(20) ); // 0.10000000000000000555
そして 2 つの数値を合計すると、その「精度の損失」が加算されます。
そのため、 0.1 + 0.2
は正確には0.3
ではありません。
JavaScriptだけではない
同じ問題は他の多くのプログラミング言語にも存在します。
PHP、Java、C、Perl、および Ruby は、同じ数値形式に基づいているため、まったく同じ結果が得られます。
この問題を回避できますか?確かに、最も信頼できる方法は、 toFixed(n) メソッドを使用して結果を丸めることです。
合計 = 0.1 + 0.2 とします。 アラート( sum.toFixed(2) ); // "0.30"
toFixed
常に文字列を返すことに注意してください。小数点以下 2 桁であることが保証されます。これは、電子ショッピングをしていて$0.30
表示する必要がある場合に実際に便利です。他のケースでは、単項プラスを使用して強制的に数値にすることができます。
合計 = 0.1 + 0.2 とします。 アラート( +sum.toFixed(2) ); // 0.3
また、一時的に数値に 100 (またはそれ以上の数値) を掛けて整数に変換し、計算してから割り戻すこともできます。次に、整数を使って計算を行うと、誤差はいくらか減少しますが、それでも割り算では誤差が生じます。
アラート( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3 アラート( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001
したがって、乗算/除算のアプローチにより誤差は軽減されますが、完全に除去されるわけではありません。
場合によっては、分数をまったく回避しようとすることもあります。ショップを扱う場合と同様に、価格をドルではなくセントで保存できます。しかし、30% の割引を適用するとどうなるでしょうか?実際には、分数を完全に回避することはほとんど不可能です。必要に応じて丸めて「尾」をカットするだけです。
面白いこと
これを実行してみてください:
// こんにちは!私は自分で増やす数字です! アラート( 9999999999999999 ); // 10000000000000000 を表示します
これには、精度の低下という同じ問題があります。数値には 64 ビットがあり、そのうちの 52 ビットは数字の格納に使用できますが、それだけでは十分ではありません。したがって、最下位桁は消えます。
JavaScript はそのようなイベントではエラーを引き起こしません。数値を目的の形式に適合させるために最善を尽くしますが、残念ながら、この形式は十分な大きさではありません。
ゼロが 2 つ
数値の内部表現のもう 1 つの面白い結果は、2 つのゼロ ( 0
と-0
が存在することです。
これは、符号が 1 ビットで表されるため、ゼロを含む任意の数値に対して設定することも設定しないこともできるためです。
ほとんどの場合、演算子はそれらを同じものとして扱うのに適しているため、区別は目立ちません。
これら 2 つの特別な数値を覚えていますか?
Infinity
(および-Infinity
) は、何よりも大きい (小さい) 特別な数値です。
NaN
エラーを表します。
これらはタイプnumber
に属しますが、「通常の」数値ではないため、それらをチェックするための特別な関数があります。
isNaN(value)
引数を数値に変換し、それがNaN
であるかどうかをテストします。
アラート( isNaN(NaN) ); // 真実 アラート( isNaN("str") ); // 真実
しかし、この機能は必要でしょうか? === NaN
比較を使用することはできないでしょうか?残念ながらそうではありません。値NaN
は、それ自体を含め、何にも等しくないという点で独特です。
アラート( NaN === NaN ); // 間違い
isFinite(value)
引数を数値に変換し、それがNaN/Infinity/-Infinity
ではなく通常の数値である場合にtrue
を返します。
アラート( isFinite("15") ); // 真実 アラート( isFinite("str") ); // false、特別な値: NaN であるため アラート( isFinite(Infinity) ); // false、特別な値: Infinity のため
isFinite
文字列値が正規の数値かどうかを検証するために使用される場合があります。
let num = +prompt("数値を入力してください", ''); // Infinity、-Infinity、または数値以外を入力しない限り true になります アラート( isFinite(num) );
isFinite
含むすべての数値関数では、空の文字列またはスペースのみの文字列は0
として扱われることに注意してください。
Number.isNaN
とNumber.isFinite
Number.isNaN メソッドと Number.isFinite メソッドは、 isNaN
とisFinite
関数のより「厳密な」バージョンです。引数を数値に自動変換しませんが、代わりにそれがnumber
型に属しているかどうかをチェックします。
Number.isNaN(value)
引数がnumber
型に属し、 NaN
である場合にtrue
返します。それ以外の場合は、 false
を返します。
アラート( Number.isNaN(NaN) ); // 真実 alert( Number.isNaN("str" / 2) ); // 真実 // 違いに注意してください: alert( Number.isNaN("str") ); // false、「str」は数値型ではなく文字列型に属するため アラート( isNaN("str") ); // true、isNaN は文字列 "str" を数値に変換し、この変換の結果として NaN を取得するため
Number.isFinite(value)
引数がnumber
型に属し、 NaN/Infinity/-Infinity
ではない場合にtrue
を返します。それ以外の場合は、 false
を返します。
アラート( Number.isFinite(123) ); // 真実 alert( Number.isFinite(Infinity) ); // 間違い アラート( Number.isFinite(2 / 0) ); // 間違い // 違いに注意してください: alert( Number.isFinite("123") ); // false、「123」は数値型ではなく文字列型に属するため アラート( isFinite("123") ); // isFinite は文字列「123」を数値 123 に変換するため、true
ある意味では、 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
。数値には内部的に符号ビットがあり、他のすべてのビットが 0 であっても異なる可能性があるため、技術的には正しいです。
それ以外のすべての場合、 Object.is(a, b)
a === b
と同じです。
Object.is
については JavaScript 仕様でよく使用されるため、ここで説明します。内部アルゴリズムが 2 つの値が完全に同じであるかどうかを比較する必要がある場合、 Object.is
(内部的には SameValue と呼ばれます) を使用します。
プラス+
またはNumber()
を使用した数値変換は厳密です。値が正確な数値でない場合、失敗します。
アラート( +"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、整数部分のみが返されます alert( parseFloat('12.3.4') ); // 12.3、2 番目のポイントで読み取りが停止します
parseInt/parseFloat
がNaN
返す場合があります。これは、数字を読み取れなかった場合に発生します。
alert( parseInt('a123') ); // NaN、最初の記号はプロセスを停止します
parseInt(str, radix)
の第二引数
parseInt()
関数にはオプションの 2 番目のパラメータがあります。これは数値体系の基数を指定するため、 parseInt
16 進数、2 進数などの文字列も解析できます。
alert( parseInt('0xff', 16) ); // 255 アラート( parseInt('ff', 16) ); // 0x なしの 255 も機能します アラート( parseInt('2n9c', 36) ); // 123456
JavaScript には、数学関数と定数の小さなライブラリを含む組み込み Math オブジェクトがあります。
いくつかの例:
Math.random()
0 ~ 1 (1 は含まない) の乱数を返します。
アラート( Math.random() ); // 0.1234567894322 アラート( Math.random() ); // 0.5435252343232 アラート( Math.random() ); // ... (任意の乱数)
Math.max(a, b, c...)
およびMath.min(a, b, c...)
任意の数の引数から最大値と最小値を返します。
alert( Math.max(3, 5, -10, 0, 1) ); // 5 アラート( Math.min(1, 2) ); // 1
Math.pow(n, power)
指定されたn
べき乗して返します。
アラート( Math.pow(2, 10) ); // 2の累乗10 = 1024
Math
オブジェクトには、三角関数など、さらに多くの関数と定数があります。これらは、Math オブジェクトのドキュメントで見つけることができます。
多くのゼロを含む数値を書き込むには:
数字にゼロのカウントを付けて"e"
を追加します。たとえば、 123e6
123
ゼロが 6 つ付いたもの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 オブジェクトを参照してください。ライブラリは非常に小さいですが、基本的なニーズはカバーできます。
重要度: 5
訪問者に 2 つの数値の入力を促し、その合計を表示するスクリプトを作成します。
デモを実行する
PS タイプには注意点があります。
let a = +prompt("最初の数字は?", ""); let b = +prompt("2 番目の数字は?", ""); アラート( a + b );
prompt
の前に単項プラス+
あることに注意してください。値はすぐに数値に変換されます。
それ以外の場合、 a
とb
文字列になり、その合計はそれらの連結になります (つまり、 "1" + "2" = "12"
。
重要度: 4
ドキュメントMath.round
とtoFixed
によると、両方とも最も近い数値に丸められます。0..4 0..4
下降し、 5..9
上昇します。
例えば:
アラート( 1.35.toFixed(1) ); // 1.4
以下の同様の例で、 6.35
が6.4
ではなく6.3
に四捨五入されるのはなぜですか?
アラート( 6.35.toFixed(1) ); // 6.3
6.35
適切に四捨五入するにはどうすればよいでしょうか?
内部的には、小数部6.35
は無限の 2 進数です。このような場合はいつものように、精度が低下して保存されます。
見てみましょう:
アラート( 6.35.toFixed(20) ); // 6.34999999999999964473
精度の低下により、数値の増加と減少の両方が発生する可能性があります。この特定のケースでは、数値が少し小さくなるため、切り捨てられています。
では、 1.35
とは何でしょうか?
アラート( 1.35.toFixed(20) ); // 1.35000000000000008882
ここでは精度の低下により数値が少し大きくなったため、切り上げられています。
6.35 を正しい方法で丸めたい場合、どうすれば6.35
の問題を解決できるでしょうか?
四捨五入する前に、これを整数に近づける必要があります。
アラート( (6.35 * 10).toFixed(20) ); // 63.50000000000000000000
63.5
には精度の損失がまったくないことに注意してください。これは、小数部分0.5
が実際には1/2
あるためです。 2
の累乗で割った分数は 2 進法で正確に表現されるので、四捨五入することができます。
アラート( Math.round(6.35 * 10) / 10 ); // 6.35 -> 63.5 -> 64(四捨五入) -> 6.4
重要度: 5
訪問者が有効な数値を入力するまで数値の入力を求める関数readNumber
を作成します。
結果の値は数値として返される必要があります。
訪問者は空行を入力するか「CANCEL」を押してプロセスを停止することもできます。その場合、関数はnull
を返す必要があります。
デモを実行する
テストを含むサンドボックスを開きます。
関数 readNumber() { 数字をあげましょう。 する { num = プロンプト("数字を入力してください?", 0); while ( !isFinite(num) ); if (num === null || num === '') は null を返します。 +num を返します。 } alert(`読み取り: ${readNumber()}`);
解決策は少し複雑ですが、 null
や空行を処理する必要があることが原因である可能性があります。
したがって、実際には「通常の数値」になるまで入力を受け付けます。 null
(キャンセル) と空行の両方も、数値形式では0
であるため、この条件に当てはまります。
停止した後は、 null
と空行を特別に扱う必要があります ( null
返す)。これらを数値に変換すると0
返されるからです。
サンドボックス内のテストを含むソリューションを開きます。
重要度: 4
このループは無限です。それは決して終わりません。なぜ?
i = 0 とします。 while (i != 10) { i += 0.2; }
それは、 i
10
に等しくないからです。
これを実行してi
の実際の値を確認します。
i = 0 とします。 while (i < 11) { i += 0.2; if (i > 9.8 && i < 10.2) アラート( i ); }
どれも正確に10
ではありません。
このようなことは、 0.2
などの分数を加算するときに精度が低下するために発生します。
結論: 小数を扱うときは等価性チェックを回避します。
重要性: 2
組み込み関数Math.random()
0
から1
までのランダムな値を作成します ( 1
は含まれません)。
関数random(min, max)
を作成して、 min
からmax
までのランダムな浮動小数点数を生成します ( max
は含まれません)。
その働きの例:
アラート(ランダム(1, 5) ); // 1.2345623452 アラート(ランダム(1, 5) ); // 3.7894332423 アラート(ランダム(1, 5) ); // 4.3435234525
間隔 0…1 のすべての値をmin
からmax
までの値に「マッピング」する必要があります。
これは 2 つの段階で実行できます。
0…1 の乱数にmax-min
を乗算すると、取り得る値の間隔は0..1
から0..max-min
まで増加します。
ここでmin
追加すると、可能な間隔はmin
からmax
までになります。
機能:
関数ランダム(最小、最大) { 最小値 + Math.random() * (最大値 - 最小値) を返します。 } アラート(ランダム(1, 5) ); アラート(ランダム(1, 5) ); アラート(ランダム(1, 5) );
重要性: 2
可能な値としてmin
とmax
両方を含むmin
からmax
までのランダムな整数を生成する関数randomInteger(min, max)
を作成します。
min..max
間隔の数値は同じ確率で出現する必要があります。
その働きの例:
アラート(ランダム整数(1, 5) ); // 1 アラート(ランダム整数(1, 5) ); // 3 アラート(ランダム整数(1, 5) ); // 5
前のタスクの解決策をベースとして使用できます。
最も単純ですが間違った解決策は、 min
からmax
までの値を生成し、それを四捨五入することです。
関数randomInteger(min, max) { rand = 最小値 + Math.random() * (最大値 - 最小値); Math.round(rand) を返します。 } アラート(ランダム整数(1, 3) );
関数は動作しますが、正しくありません。エッジ値のmin
とmax
を取得する確率は、他の確率の 2 分の 1 です。
上記の例を何度も実行すると、 2
最も頻繁に表示されることが簡単にわかります。
これは、 Math.round()
区間1..3
から乱数を取得し、次のように丸めるために発生します。
1 ... から 1.4999999999 までの値は 1 になります 1.5 ... から 2.4999999999 までの値は 2 になります 2.5 ... ~ 2.9999999999 の値は 3 になります
これで、 1
2
よりも 2 倍少ない値を取得することがはっきりとわかります。 3
も同様です。
この課題に対する正しい解決策はたくさんあります。その 1 つは、間隔の境界を調整することです。同じ間隔を確保するには、 0.5 to 3.5
の値を生成し、必要な確率をエッジに追加します。
関数randomInteger(min, max) { // 現在、rand は (min-0.5) から (max+0.5) になります。 rand = min - 0.5 + Math.random() * (max - min + 1); とします。 Math.round(rand) を返します。 } アラート(ランダム整数(1, 3) );
別の方法として、 min
からmax+1
までの乱数にMath.floor
使用することもできます。
関数randomInteger(min, max) { // ここで rand は min から (max+1) までです rand = min + Math.random() * (max + 1 - min); とします。 Math.floor(rand) を返します。 } アラート(ランダム整数(1, 3) );
これで、すべての間隔が次のようにマッピングされます。
1 ... から 1.9999999999 までの値は 1 になります 2 ... から 2.9999999999 までの値は 2 になります 3 ... から 3.9999999999 までの値は 3 になります
すべての間隔は同じ長さであるため、最終的な分布は均一になります。