0より大きい最小の数は何ですか? これは、「存在しない」と「それは依存する」の間のどこかで複雑な答えを持つ単純な質問の1つです。
あなたが数学者に尋ねると、彼らは数学を壊すのでそのような数はあり得ないとあなたに言います。 数値nがある場合、nは0の後の最小の数値であり、nはすでに最小であるため、数値n/2は存在できません。 これは、除算自体が分解されることを意味しますが、数学者は好きではありません。
コンピュータに尋ねると、実際に答えが得られます。 現実の世界とは対照的に、コンピュータは無限の数を持っていません。 コンピュータはメモリレジスタに番号を格納し、各レジスタには固定ビット数があります。 あなただけの三桁を持っていた場合を想像してみてく あなたが表すことができる最大の数は999です。 これは、コンピュータで何が起こるかです。
これは、コンピュータ内の整数の集合が桁数によって制限されることを意味します。 コンピュータでは、間違いなく最大の数があります(たとえば、CではINT_MAX
)。 これは最大桁数の数値で、すべてがバイナリ1
に設定されています。 8ビットシステムでは、この番号は11111111
になります。
負の数を含めることで、問題をさらに複雑にすることができます。 8ビットのデータがある場合は、最初のビットを使用して数値の符号を表すことができます。 プラスの場合は0
、マイナスの場合は1
です。 今、私たちは私たちの数字のための7ビットを持っているので、最大の数は011111111
私たちの前の最大の数よりも小さいです。
まだ終わっていません。 また、10進数を表す必要があります。 たとえ0.12が小さい数であっても、123のように3桁の数字があります。 違いは、私たちが考える必要があるもう一つのことがあることです:小数点の点、基数点とも呼ばれます。 数字と基数点の位置の両方を格納する必要があります。
整数はどれくらいの大きさに制限されますが、10進数はサイズと精度の両方に制限されています。 桁数が固定されている場合、小数点の後に置くことができる桁数は非常に多くあります。 これが、コンピュータが10進数を丸める必要がある理由です。
それでは、これらの10進数をどのように格納するのですか? コンピュータは整数のみを理解するので、整数のみを使用して10進数を格納する方法が必要です。
私たちは番号3.14
を持っているとします。 数字のすべての数字を書くことから始めましょう。 314
を取得しました。 それはスタートです。 私たちは、10のべき乗を掛けることによって、その数の周りに小数点を「移動」することができることを知っています。 314 * 10^-1
は31.4、314 * 10^-2
は3.14です。
数3.14を表すために必要なのは、314、10、-2の3つの整数だけです。 314は仮数と呼ばれるもので、これは書き出された数字のすべての数字です。
10は基数または基数と呼ばれます。 私たちは、10のべき乗を掛けることによって、10進数のドットを基数10の数字の周りに移動できることを知っています。 すべての数の基数に対して同じことが機能します: 基数2(またはバイナリ)では、2のべき乗を乗算してドットをシフトすることができます。
シフトするべき乗は指数と呼ばれ、小数点のドットがどこにあるかを示します。
すべての10進数を単純な式でこれらの3つの数値として書くことができます:
number = significand * base^exponent3.14 = 314 * 10^-2
32.8 = 328 * 10^-1
コンピュータは、符号、指数、および仮数を32または64ビット桁の単一の文字列に格納することによって、10進数を格納します。 通常、符号には1ビット、指数を格納するには11ビット、仮数を格納するには53ビットがあり、合計で64ビットになります。
それを念頭に置いて、私たちの質問に戻りましょう: ゼロ以外の最小の数は何ですか? 余裕のある数字が3桁しかない場合、可能な限り最小の数字は0.01です。 四桁では0.001です。 仮数部は常に同じで、指数部のみが変更されます。
必要なのは、1
の仮数です。0
の後の最小の仮数なので、1
必要なのは、0
の後の仮数です。 次に、小数点をできる限り左にシフトする必要があります。 これを行うには、最小の(最も負の)可能な指数が必要です。
どのように小さい、メモリ内の数のレイアウトに依存します。 指数に11ビットがある場合、10ビットの長さの数値のみを書き出すことができ、1ビットは符号用に予約されています。 64ビットシステムでは、最小の指数は-308
です。
最終的には、64ビットシステムで可能な最小の数は1 * 10^-308
の周りになります。 それは小さいです!
私たちは、最小の数があることを確立しました。 この数は私達が私達のコンピュータを信頼してもいいかどの位私達に告げる。 非常に大きな数字や非常に正確な数字を必要とする何かをしている場合は、この数字を念頭に置く必要があります。
私たちが計算したのは、最後の場所の単位、つまりulpと呼ばれる0です。 別に本当にクールな言葉であることから、ulpは、コンピュータ内の二つの数字の間の最小距離が何であるかを教えてくれます。 0と次の数値の間の最小距離である0のulpを計算しました。
計算された値を0に追加して比較しようとすると、同じ数にはなりません。 ただし、ulpよりも小さい値を追加すると、コンピュータに関する限り、同じ数値になります。
print(0 == 0 + ulp(0)) // false
print(0 == 0 + ulp(0) / 2) // true
数値にゼロ以外の値を追加すると異なる数値が生成されることは明らかですが、コンピュータはどこかで丸める必要があるため、2つの数値が同
コンピュータシステムをより簡単に比較するために、1のulpを使用し、それをマシンイプシロンと呼びます。 マシンepsilonがわかったら、次の式で他のulpを計算できます:
ulp(x) = machine epsilon * radix^exponent(x)
私たちが計算した値は非常に小さいので、コーディング中にその制限に達することはありません。 しかし、0の値を計算しました。 小数点の左側に必要な桁数が多いほど、右側に必要な桁数は少なくなります。 これは、数値が大きいほど精度が低くなることを意味します。 言い換えると、ulpは指数の直接関数です。 小数点を右にシフトすると、ulpが増加し、精度が失われます。
次回コードで奇妙な浮動小数点エラーが発生したときに、この情報が役立つことを願っています。 覚えておいて、コンピュータはかなり強力ですが、コンピュータでさえ限界があります。