2023年12月27日

数値

現代のJavaScriptには、2種類の数値型があります。

  1. JavaScriptの通常の数値は、64ビット形式のIEEE-754(「倍精度浮動小数点数」としても知られています)で格納されます。これらは私たちがほとんどの場合使用する数値であり、この章でそれらについて説明します。

  2. 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

base2 から 36 の範囲で変動する可能性があります。デフォルトでは、10 です。

一般的なユースケースは

  • base=16 は、16進数の色、文字エンコーディングなどに使用され、数字は 0..9 または A..F になります。

  • base=2 は、主にビット単位の操作をデバッグするために使用され、数字は 0 または 1 になります。

  • base=36 は最大で、数字は 0..9 または A..Z になります。ラテンアルファベット全体が数値を表すために使用されます。36 の面白いですが便利なケースは、長い数値識別子を、たとえば、短いURLを作成するなど、より短いものに変換する必要がある場合です。base 36 の数値システムで簡単に表すことができます

    alert( 123456..toString(36) ); // 2n9c
メソッドを呼び出すための2つのドット

123456..toString(36) の2つのドットはタイプミスではないことに注意してください。上記の例の toString のように、数値上で直接メソッドを呼び出す場合は、その後に2つのドット .. を配置する必要があります。

1つのドットを配置した場合:123456.toString(36)、最初のドットの後に小数点部分が続くため、エラーが発生します。もう1つのドットを配置すると、JavaScriptは小数点部分が空であり、メソッドに進むことを認識します。

また、(123456).toString(36) と書くこともできます。

丸め

数値を使用する場合に最も使用される操作の1つは丸めです。

丸めにはいくつかの組み込み関数があります

Math.floor
切り捨て: 3.13 になり、-1.1-2 になります。
Math.ceil
切り上げ: 3.14 になり、-1.1-1 になります。
Math.round
最も近い整数に丸めます: 3.13 になり、3.64 になり、中間の場合: 3.54 に丸められます。
Math.trunc (Internet Explorerではサポートされていません)
丸めを行わずに小数点以下のすべてを削除します: 3.13 になり、-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つの方法があります

  1. 掛け算と割り算。

    たとえば、数値を小数点以下の2桁に丸めるには、数値を 100 で掛け、丸め関数を呼び出してから、それを割り戻します。

    let num = 1.23456;
    
    alert( Math.round(num * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23
  2. メソッド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.10.2 の合計が 0.3 であるかどうかを確認すると、false が返されます。

おかしい!0.3 でない場合は何なのでしょうか?

alert( 0.1 + 0.2 ); // 0.30000000000000004

うわ!あなたがeショップサイトを作成していて、訪問者がカートに $0.10$0.20 の商品を入れたと想像してみてください。注文の合計は $0.30000000000000004 になります。それは誰にとっても驚きでしょう。

しかし、なぜこうなるのでしょうか?

数値は、1と0のビットのシーケンスであるバイナリ形式でメモリに格納されます。しかし、10進数のシステムでは単純に見える 0.10.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 にならない理由です。

JavaScriptだけではない

同じ問題が他の多くのプログラミング言語にも存在します。

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はこのような場合にエラーを発生させません。数値を希望の形式に合わせようと最善を尽くしますが、残念ながら、この形式は十分な大きさではありません。

2つのゼロ

数値の内部表現のもう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.isNaNNumber.isFinite

Number.isNaNNumber.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.isNaNNumber.isFinite は、isNaN および isFinite 関数よりも単純でわかりやすいです。実際には、isNaNisFinite の方が記述が短いため、ほとんど使用されます。

Object.is との比較

=== のように値を比較する特別な組み込みメソッド Object.is がありますが、2つのエッジケースでより信頼性が高くなります。

  1. NaN で動作します。Object.is(NaN, NaN) === true であり、良いことです。
  2. 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€" のように数値を取り出したい場合もあります。

これが、parseIntparseFloat がある理由です。

これらは、それ以上読み取れなくなるまで文字列から数値を「読み取り」ます。エラーが発生した場合、収集された数値が返されます。関数 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/parseFloatNaN を返す状況があります。これは、数字が読み取れなかった場合に発生します。

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つのゼロが付いた 123123000000 と同じです。
  • "e" の後の負の数は、その数値を与えられたゼロの数で1で割ることを意味します。例:123e-60.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 でないかどうかをテストします。

12pt100px のような値を数値に変換する場合

  • 文字列から数値を読み取り、エラーの前に読み取れた値を返す「ソフト」変換には、parseInt/parseFloat を使用します。

分数の場合

  • Math.floorMath.ceilMath.truncMath.round、または num.toFixed(precision) を使用して丸めます。
  • 分数で作業するときには、精度が失われることを必ず覚えておいてください。

その他の数学関数

  • 必要な場合は、Math オブジェクトを参照してください。ライブラリは非常に小さいですが、基本的なニーズをカバーできます。

タスク

重要度:5

訪問者に2つの数値を入力するように促し、それらの合計を表示するスクリプトを作成します。

デモを実行する

追伸。型に注意すべき点があります。

let a = +prompt("The first number?", "");
let b = +prompt("The second number?", "");

alert( a + b );

prompt の前の単項プラス + に注目してください。これにより、値がすぐに数値に変換されます。

そうしないと、ab は文字列になり、それらの合計は連結になります。つまり、"1" + "2" = "12" になります。

重要度:4

ドキュメントによると、Math.roundtoFixed はどちらも最も近い数値に丸めます。0..4 は切り下げ、5..9 は切り上げます。

例えば

alert( 1.35.toFixed(1) ); // 1.4

次の同様の例で、なぜ 6.356.4 ではなく 6.3 に丸められるのでしょうか?

alert( 6.35.toFixed(1) ); // 6.3

6.35 を正しく丸めるにはどうすればよいですか?

内部的には、10進数 6.35 は無限の2進数です。そのような場合と同様に、精度が失われた状態で格納されます。

確認してみましょう。

alert( 6.35.toFixed(20) ); // 6.34999999999999964473

精度の損失は、数値の増加と減少の両方につながる可能性があります。この特定のケースでは、数値がわずかに小さくなるため、切り捨てられました。

では、1.35 はどうでしょうか?

alert( 1.35.toFixed(20) ); // 1.35000000000000008882

ここでは、精度の損失によって数値が少し大きくなったため、切り上げられました。

6.35 を正しく丸めたい場合、この問題をどのように解決できますか?

丸める前に整数に近づける必要があります。

alert( (6.35 * 10).toFixed(20) ); // 63.50000000000000000000

63.5 には精度の損失がまったくありません。これは、小数点部分 0.5 が実際には 1/2 であるためです。2 のべき乗で割られた分数は、2進数システムで正確に表現されます。これで丸めることができます。

alert( Math.round(6.35 * 10) / 10 ); // 6.35 -> 63.5 -> 64(rounded) -> 6.4
重要度:5

訪問者が有効な数値を入力するまで数値を求める関数 readNumber を作成します。

結果の値は数値として返す必要があります。

訪問者は、空行を入力するか「キャンセル」を押してプロセスを停止することもできます。その場合、関数は null を返す必要があります。

デモを実行する

テストを含むサンドボックスを開きます。

function readNumber() {
  let num;

  do {
    num = prompt("Enter a number please?", 0);
  } while ( !isFinite(num) );

  if (num === null || num === '') return null;

  return +num;
}

alert(`Read: ${readNumber()}`);

null /空行を処理する必要があるため、解決策は少し複雑になる可能性があります。

したがって、実際には「通常の数値」になるまで入力を受け付けます。null(キャンセル)と空行も数値形式では 0 であるため、この条件に適合します。

停止した後、null と空行は特別に扱う必要があります(null を返す)。それらを数値に変換すると 0 が返されるためです。

サンドボックスでテストを含むソリューションを開きます。

重要度:4

このループは無限です。決して終わりません。なぜですか?

let i = 0;
while (i != 10) {
  i += 0.2;
}

それは、i が決して 10 に等しくならないためです。

それを実行して、i実際の値を確認してください。

let i = 0;
while (i < 11) {
  i += 0.2;
  if (i > 9.8 && i < 10.2) alert( i );
}

それらのどれも正確には 10 ではありません。

このようなことは、0.2 のような分数を加算するときの精度損失のために発生します。

結論:10進数を使用する場合は、等価性チェックを避けてください。

重要度:2

組み込み関数 Math.random() は、0 から 11 は含まない)までの乱数を生成します。

min から maxmax は含まない)までの乱数を生成する関数 random(min, max) を作成します。

動作例

alert( random(1, 5) ); // 1.2345623452
alert( random(1, 5) ); // 3.7894332423
alert( random(1, 5) ); // 4.3435234525

0...1 の間隔のすべての値を min から max までの値に「マッピング」する必要があります。

これは2段階で実行できます。

  1. 0...1 からの乱数に max-min を乗算すると、可能な値の間隔が 0..1 から 0..max-min に広がります。
  2. さて、min を加えると、可能な区間は min から max になります。

関数

function random(min, max) {
  return min + Math.random() * (max - min);
}

alert( random(1, 5) );
alert( random(1, 5) );
alert( random(1, 5) );
重要度:2

randomInteger(min, max) という関数を作成してください。この関数は、min から max までのランダムな *整数* を生成し、minmax の両方を可能な値として含みます。

区間 min..max 内の任意の数が同じ確率で出現する必要があります。

動作例

alert( randomInteger(1, 5) ); // 1
alert( randomInteger(1, 5) ); // 3
alert( randomInteger(1, 5) ); // 5

前のタスクの 解決策 をベースとして使用できます。

シンプルだが間違った解決策

最も単純ですが、間違った解決策は、min から max までの値を生成し、それを丸めることです。

function randomInteger(min, max) {
  let rand = min + Math.random() * (max - min);
  return Math.round(rand);
}

alert( randomInteger(1, 3) );

関数は動作しますが、正しくありません。エッジの値 minmax を取得する確率は、他の値の2倍小さくなります。

上記の例を何度も実行すると、2 が最も頻繁に表示されることが簡単にわかります。

これは、Math.round() が区間 1..3 からランダムな数値を取得し、次のように丸めるために発生します。

values from 1    ... to 1.4999999999  become 1
values from 1.5  ... to 2.4999999999  become 2
values from 2.5  ... to 2.9999999999  become 3

これで、12 より2倍少ない値を取得していることが明確にわかります。3 も同様です。

正しい解決策

タスクに対する多くの正しい解決策があります。その1つは、区間の境界を調整することです。同じ区間を確保するために、0.5 から 3.5 までの値を生成して、エッジに必要な確率を追加できます。

function randomInteger(min, max) {
  // now rand is from  (min-0.5) to (max+0.5)
  let rand = min - 0.5 + Math.random() * (max - min + 1);
  return Math.round(rand);
}

alert( randomInteger(1, 3) );

別の方法としては、min から max+1 までの乱数に Math.floor を使用することが考えられます。

function randomInteger(min, max) {
  // here rand is from min to (max+1)
  let rand = min + Math.random() * (max + 1 - min);
  return Math.floor(rand);
}

alert( randomInteger(1, 3) );

これで、すべての区間がこのようにマッピングされます。

values from 1  ... to 1.9999999999  become 1
values from 2  ... to 2.9999999999  become 2
values from 3  ... to 3.9999999999  become 3

すべての区間は同じ長さであるため、最終的な分布は均一になります。

チュートリアルマップ

コメント

コメントする前にこれを読んでください…
  • 改善するための提案がある場合は、コメントする代わりに GitHub issue を送信するか、プルリクエストをしてください。
  • 記事の内容を理解できない場合は、詳しく説明してください。
  • 数単語のコードを挿入するには <code> タグを使用し、複数行の場合は <pre> タグで囲み、10行以上場合はサンドボックス (plnkr, jsbin, codepen…) を使用してください。