TL;DR
オプション #1 またはオプション #2 のいずれかを選択します。どちらにも違いはありません。操作が面倒なので、オプション #3 は使用しないでください。
浮動小数点数には固有の不正確さがあると主張しています。これは、最初に少し検討する価値があると思います。
数値を表すための数値システム(紙、コンピューター回路、またはその他の場所) を決定する際には、考慮すべき 2 つの個別の問題があります。
その基礎; と
そのフォーマット。
ベースを選んで、どんなベースでも…
有限空間によって制限されるため、無限集合の任意のメンバーを表すことはできません。 たとえば、購入する紙の量や手書きの文字がどれほど小さいかに関係なく、指定されたスペースに収まらない整数を見つけることは常に可能です (紙がなくなるまで余分な数字を追加し続けることができます)。したがって、整数では、通常、有限のスペースを特定の間隔内に収まるもののみを表すように制限します。たとえば、正/負の符号と 3 桁のスペースがある場合、間隔 に制限することがあります[-999,+999]
。
空でないすべての間隔には、実数の無限のセットが含まれます。 言い換えれば、どの区間が実数を引き継ぐかに関係なく —それ[-999,+999]
、[0,1]
、[0.000001,0.000002]
またはその他のものであっても — その区間内には実数の無限のセットが存在します ((非ゼロの) 小数桁を追加し続けるだけで済みます)! したがって、任意の実数は常に、有限空間で表現できるものに「丸める」
有限空間で表現できる実数のセットは、使用される数値システムによって異なります。 私たちの (おなじみの) 10 進法の定位置 システムでは、有限スペースは 2 分の 1 ( ) には十分ですが、3 分の 1 ( ) には十分ではありません。対照的に、(あまりなじみのない) 9進法では、逆です (同じ数字はそれぞれとです)。これらすべての結果として、10 進数の位置でわずかなスペースだけを使用して表現できる数値がいくつかあります (したがって、次のように表示されます)。0.510
0.33333…10
0.44444…9
0.39
たとえば、10 分の 1 を正確に格納するには、実際には無限のバイナリ回路が必要になります (したがって、デジタルの友人にはあまり「丸く」見えません)。特に、2 は 10 の因数であるため、同じことは逆には成り立ちません。有限の 2 進数で表現できる数値は、有限の 10 進数でも表現できます。
連続量については、これ以上のことはできません。最終的に、そのような量は、何らかの数値システム で有限表現を使用する必要があります。そのシステムがたまたまコンピューター回路、人間の指、または他の何かで簡単であるかどうかは任意です。どのシステムを使用する場合でも、値は丸められ、したがって、常に「表現エラー」になります。
言い換えれば、たとえ完全に正確な測定器を持っていたとしても (これは物理的に不可能です)、それが報告する測定値は、たまたまそのディスプレイに収まる数値に丸められています (使用する基数は何であれ、通常は 10 進数、明らかな理由で)。したがって、「86.2 オンス」は実際には「86.2 オンス」ではなく、「86.1500000... オンスと 86.2499999... オンスの間の何か」を表しています。(実際には、計測器は不完全であるため、実際の値がその間隔内に収まるというある程度の信頼があるとしか言いようがありませんが、それは明らかにここでのポイントからいくらか逸脱しています)。
しかし、離散量についてはもっとうまくやることができます。そのような値は「任意の実数」ではないため、上記のいずれも適用されません。それらは、定義された数値システムで正確に表すことができます。有限の長さは、不正確な数に丸められます)。コンピュータは、数値を文字列として表現することで (非効率的に) このような状況を処理できます。たとえば、ASCIIまたはBCDエンコーディングを検討してください。
フォーマットを適用…
これは数値システムの (やや恣意的な) 基準のプロパティであるため、値が「丸みを帯びている」ように見えるかどうかは、その精度には関係ありません。これは非常に重要な観察であり、多くの人の直感に反しています (これが、上記の数値的根拠の説明に多くの時間を費やした理由です)。
代わりに、精度は表現の有効数字の数によって決まります。少なくとも、私たちが正しいと考える有効数字の数だけ値を記録できるストレージ形式が必要です。およびとして記述された場合に正しいと見なされる値の例を挙げる86.2
と0.0000862
、最も一般的な 2 つのオプションは次のとおりです。
有効桁数が大きさに依存する固定小数点 : たとえば、固定小数点 5 桁の表現では、値は および として格納されます86.20000
(0.00009
したがって、それぞれ有効桁数が 7 桁と 1 桁になります)。この例では、後者の値で精度が失われています (実際、重要なものをまったく表現できなくなるのにそれほど時間はかかりませんでした)。前者の値は偽の precisionを格納しました。これは、有限のスペースを無駄にします (実際、値が大きくなりすぎてストレージ容量をオーバーフローするのにそれほど時間はかかりません)。
この形式が適切な場合の一般的な例は、会計システムの場合です。通常、金額は、その大きさに関係なくペニー単位で追跡する必要があります (したがって、小さな値には必要な精度が低く、大きな値にはより高い精度が必要です)。たまたま、通貨は通常、離散的 (ペニーは分割できない) と見なされるため、これは、上記の表現エラーを回避するために特定の基準 (ほとんどの最新の通貨では 10 進数) が望ましい状況の良い例でもあります。
通常、値を共通の分母に対する商として扱い、分子を整数として格納することにより、固定小数点ストレージを実装します。この例では、共通分母は 10 586.20000
である可能性があるため、との代わりに0.00009
整数8620000
を格納し、9
で割る必要があることに注意してください100000
。
浮動小数点。有効桁数は大きさに関係なく一定です86.200
。たとえば、有効桁数 5 桁の 10 進数表現では、値はandとして格納されます0.000086200
(定義により、どちらの場合も有効桁数が 5 桁になります)。この例では、両方の値が精度を損なうことなく格納されています。また、どちらも同じ量の誤った精度を持っているため、無駄が少なくなります (したがって、有限空間を使用して、大小の値のはるかに広い範囲を表すことができます)。
この形式が適切な場合の一般的な例は、実世界の測定値を記録する場合です。測定器の精度 (すべて系統誤差とランダム誤差の両方に悩まされます) は、スケールに関係なくかなり一定であるため、十分な有効数字 (通常は約 3または 4 桁)、基数の変更によって別の数値に丸められた場合でも、精度が失われることはまったくありません。
通常、値を整数の仮数と整数の指数として扱うことにより、浮動小数点ストレージを実装します。この例では、有意86200
桁は両方の値に対応し、(基数 10) の指数はそれぞれ-4
とになり-9
ます。
しかし、私たちのコンピューターで使用されている浮動小数点ストレージ形式はどのくらい正確なのでしょうか?
IEEE754 の単精度 (binary32) 浮動小数点数には、24 ビットまたは(7 桁以上) の有効桁数があります。つまり、許容誤差は 未満です。つまり、「 」と言うより正確です。log10(224)
±0.000006%
86.20000
IEEE754 の倍精度 (binary64) 浮動小数点数は、有効桁数が 53 ビット(ほぼ 16) 桁です。つまり、「 」と言うより正確です。log10(253)
±0.00000000000001%
86.2000000000000
認識すべき最も重要なことは、これらの形式はそれぞれ「86.2」と言うよりも 1万倍以上、1 兆倍以上正確であるということです。無視してください: これについてはすぐに詳しく説明します)!
また、固定小数点形式と浮動小数点形式の両方で、値が形式でサポートされているよりも正確にわかっている場合、精度が失われることに注意してください。 このような丸め誤差は、算術演算で伝播して、明らかに誤った結果を生成する可能性があります(浮動小数点数の「固有の不正確さ」への参照を説明していることは間違いありません) 。有効数字 5 桁の浮動小数点では、 ではなく生成されます。1⁄3 × 3000
999.99000
1000.00000
1⁄7 − 7⁄50
0.0028600
0.0028571
数値解析の分野は、これらの影響を理解することに専念していますが、終了が保証されている計算方法では無限の精度を提供できないため、使用可能なシステム (頭の中で計算を実行する場合でも) はこのような問題に対して脆弱であることを認識することが重要です。 : たとえば、円の面積を計算する方法を考えてみましょう。π に使用される値の精度が必然的に失われ、結果に反映されます。
結論
実際の測定では、バイナリ浮動小数点を使用する必要があります。これは、高速でコンパクトで、非常に正確であり、他の何よりも悪くありません (開始した 10 進バージョンを含む)。MySQL の浮動小数点データ型は IEEE754 であるため、これはまさにそれらが提供するものです。
通貨アプリケーションはデナリ固定小数点を使用する必要があります。これは遅く、メモリを浪費しますが、値が不正確な量に丸められないことと、多額の金額でペニーが失われないことの両方を保証します。MySQL の固定小数点データ型は BCD でエンコードされた文字列であるため、これはまさにそれらが提供するものです。
最後に、プログラミング言語は通常、バイナリ浮動小数点型を使用して小数の値を表すことに注意してください。そのため、データベースに別の形式で値を格納する場合は、値をアプリケーションに取り込む方法に注意する必要があります。に伴うその後の問題) インターフェイスで。
この場合、どのオプションが最適ですか?
「不正確さ」についてあまり心配することなく、浮動小数点型に値を安全に格納できる (そして格納する必要がある)ことを確信できたと思いますか? これらは、薄っぺらな 3 桁の 10 進数表現よりも正確であることを忘れないでください。誤った精度を無視するだけで済みます (ただし、固定小数点 10 進数形式を使用している場合でも、常にそれを行う必要があります) 。
あなたの質問については、オプション 3 ではなくオプション 1 または 2 のいずれかを選択すると、比較が容易になります (たとえば、最大質量を見つけるには を使用できますMAX(mass)
が、2 つの列で効率的に行うにはネストが必要になります)。
これら 2 つの間で、どちらを選択しても問題ありません。浮動小数点数は、そのスケールに関係なく、一定数の有効ビットで格納されます。
さらに、一般的なケースでは、一部の値がオプション 1 を使用して元の 10 進数表現に近い 2 進数に丸められると同時に、オプション 2 を使用して元の 10 進数表現に近い 2 進数に丸められる値が発生する可能性があります。このような表現エラーは、常に無視されるべき誤った精度内でのみ明らかになることがすぐにわかります。
ただし、この場合、たまたま 16 オンスから 1 ポンド (および 16 は 2 のべき乗) があるため、2 つのアプローチを使用した元の 10 進数値と格納された 2 進数との相対的な違いは同じです。
5.387510
(あなたの質問で述べたように) として binary32 float に格納されます(これは です): これは元の値からのものです (ただし、上記で説明したように、「元の値」はすでに物理量のかなりお粗末な表現でした)を表します)。5.3367187510
101.0110001100110011001102
5.3874998092651367187510
0.0000036%
binary32 float が格納する精度は 10 進数で 7 桁のみであることを認識しているため、コンパイラは、8 桁目以降のすべてが間違いなく偽の精度であることを確実に認識しており、したがって、入力値がそれ以上の精度を必要としない限り、すべてのケースで無視する必要があります。それよりも精度が高い場合(もしそうなら、binary32 は明らかに間違った形式の選択でした)、これにより、元の値と同じように見える 10 進数値への戻りが保証されます: . ただし、ドメイン知識を実際に適用する必要があります5.38750010
この時点で (すべてのストレージ形式で行う必要があるため)、これらの 2 つの末尾のゼロなど、存在する可能性のあるさらに誤った精度を破棄します。
86.210
としてバイナリ32フロートに格納されます(これはです):これも元の値からのものです。前と同様に、偽の精度を無視して元の入力に戻ります。1010110.001100110011001102
86.199996948242187510
0.0000036%
基数ポイントの配置(4 ビット離れている)を除いて、数値のバイナリ表現がどのように同一であるかに注目してください。
101.0110 00110011001100110
101 0110.00110011001100110
これは、5.3875 × 2 4 = 86.2 であるためです。
余談ですが、私は (イギリス人ではありますが) ヨーロッパ人なので、帝国単位の測定単位にも強い嫌悪感を抱いています。異なるスケールの値を扱うのはとても厄介です。ほとんどの場合、質量をSI 単位(キログラムやグラムなど) で保存し、アプリケーションのプレゼンテーション レイヤー内で必要に応じて帝国単位に変換します。さらに、SI 単位を厳守することで、1億 2,500 万ドルの損失を回避できる日が来るかもしれません。