質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.23%

浮動小数点数の誤差

受付中

回答 7

投稿

  • 評価
  • クリップ 4
  • VIEW 2,308

mightyMask

score 125

浮動小数点数で桁数の大きな数を扱うと誤差が出てしまいますね。
これは仕方の無い事です。

私はプログラミング初学者のため、いろいろ解説サイトを見て回る訳ですが、この誤差に対する問題に対処するのが相当難しいように説明されています。
銀行などでは誤差を小さくするために、~~の様な仕組みを使っているといった様な解説も見かけました。
以下の様なデータ構造なら、多少の誤差で大問題になりかねない場合、処理速度より精度を優先させたい場合、誤差を気にせずプログラムを書きたい場合等には有用なデータ構造だと思うのですが、なぜこの様に実装する例が無いのでしょうか。

誤差の出ないデータ構造

・有理数クラス
実数クラスを継承しています。
浮動小数点型は使用せず、分子(整数)と分母(自然数)の2つのメンバを持つクラスです。

・実数クラス
無理数を扱いたい場合は、内部では人間が計算するときと同じように、円周率などは3.14...という定数ではなくπという状態を保持し、∛4 なども有理数に変換させず、そのままの形で保持しておきます。

累乗根, 円周率, ネイピア数 等 以外の、人間にも解釈できないような無理数を扱うことは不可能ですが、それが原因で問題が起きる事はないような気がします。
少なくとも有理数に関しては、誤差が出ない事を保証できると思います。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 7

+7

例は多くはないのですが、質問者さんがおっしゃるようなシステムは存在します。

・有理数クラス

比較的、我々に身近なものは計算機言語ということになりますが、有理数を標準で扱うことのできる言語の一つとしてCommon Lispがあります。質問者さんがおっしゃるとおりの方法、分母と分子を任意精度整数(※)で表現することにより厳密な有理数を表現しています。

(※:もちろん任意精度整数は無限精度ではありません。満足できる程度の「巨大な有限」を任意精度と表現しています。)

・円周率をπで、・・・
・累乗根, 円周率, ネイピア数 等...

初等関数などを誤差付きの浮動小数点数で計算してしまわず、その意味を保存したまま代数的な演算を行えるというイメージを持たれたのだと思います。そうした計算を目的とした数式処理システムが存在します。例えばMathematicaなどの例があります。

なぜこの様に実装する例が無いのでしょうか。

前述のように実際に存在しているのですが、計算機を用いる応用としてポピュラーとは言えません。そこには理由があり、計算機での計算は数式を厳密に扱うことが主要な目的ではなく、現実の問題を解決することが主要な目的だからと言えると思います。任意の計算問題は厳密さと計算速度や計算機リソースとのバランスにより方法論の優劣が検討されます。

銀行の利率計算に有理数を用いて厳密解を出したとしても円(銭?)未満の厳密さは現実には必要性は低いと考えられるでしょう。またロケットを発射する際の軌道計算をいくら厳密にしても装置の機構・加工精度や気象などの不確定要因などを全て厳密に計算に盛り込むのは不可能ですね。あるいは人間の感覚で識別できる程度の精度を狙って、音声波形を16bit程度に離散化して沢山の楽曲をコンパクトな携帯機器に記録したり、映像の画素単位を24bit程度の数値で扱い特定の電波帯域で多数のチャンネルで配送したりといった具合に、データ量を小さくすることを主眼に考えられている場面も多いです。

多くの応用で計算が一定の誤差を許容した前提で行われるのはこうした背景があるからと言えるでしょう。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+5

有理数型または有理数クラスは多くの言語で標準またはライブラリとして既にあります。

組み込み

標準ライブラリ

外部ライブラリ


なお、有理数の処理は、途中で無理数になるような計算が無い限り、誤差は全くありませんが、Zuishinさんが言う通りかなり重い処理です。また、doubleでの誤差が問題になるような約数が大きな数の処理では多倍長整数を扱えないとオーバーフローしてしまうので意味がありません。Scheme、Ruby、Haskell、Pythonはそもそも多倍長整数があります。boostのrationalはboostのmultiprecisionと一緒に使うべきでしょう。

また、十進数表記での誤差の範囲がわかっている場合は任意精度浮動小数点数(Rubyのbigdecimal、Pythonのdecimal、C++のMultiprecision - boost等)を使った方が良い場合があります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

・有理数クラス 
分数として表現できない数はどうするのでしょうか?
・実数クラス
2/3+√4という答えのままにするのでしょうか?
計算しないままそのままの形で表現するのに必要な記憶エリアはどのくらいでしょう。

2/3+√4なら2と3,4で3つ要りますね。
2/3+√4と2/5+√7はどちらが大きいと判断するのでしょうか?

誤差は出ませんが計算しないことで処理ができなくなってしまいますね。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

double は内部では 10 進数でないため、大きな数でなくても 10 進数への変換によって誤差が出ます。そのため誤差が大きな問題となる場合には decimal が使われます。

分数クラスは実装されている言語も多いと思いますが、約分・通分に時間がかかるために概数より重くなります。そのため用途が限られ、double の上位互換とはなりません。
例えば 1/2 + 2/3 を計算するのに乗算が二回必要で、結果を約分するのには因数分解も必要になります。これは何万回もループするような場合、大きな差になります。約分しなければ計算するたびに大きな数になってすぐにオーバーフローして破綻します。

定数を展開せず定数のまま扱えるクラスというものは私は知りませんが、探せばあるかもしれません。ただしこれも用途が限られてくると思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

数式をあつかうことができるシステムもあります。

参考情報

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

多少の誤差で大問題になりかねない場合

と言うもの自体が、そうそう存在しないのではないかと考えています。

たとえば、機械設計では必要な強度に対して安全率を見て設計しますし、計算を丸める場合も「より安全な方」へ丸めるようにするだけで、実用上の問題は発生しません。それに、実際の機械加工の精度にも限界があります。

銀行などの通貨計算の場合、通貨には最低単位が決まっていますので、それより下の位については、結局丸めるしかありません。少し下の位で固定小数点数として計算すれば無視できます。

また、整数に関しては多倍長整数の技術は比較的安定して存在しますので、メモリや実行時間が許す限り問題なく計算できます。

結局問題になりそうなのは、「円周率を○桁求める」とか「無理数の性質を吟味するために、細かい桁まできっちり出す」というような、ごく特殊な状況しかないのではないかと考えています。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

・有理数クラス

{ x[0] = 2/3, x[k+1]= x[k] * x[k] }
という漸化式を逐次計算するプログラムを考えます。

x[1] = 4 / 9
x[2] = 16 / 81
x[3] = 256 / 6561
x[4] = 65536 / 43046721
x[5] = 4294967296 / 1853020188851841
x[6] = 18446744073709551616 / 3433683820292512484657849089281

6回目の計算で64ビット整数で表現できる値を超えました。

  • 計算によっては、約分によって分子・分母が小さくなることがあるが、稀なケースであり、分子・分母が大きくなるケースが多い。
  • 一般には、計算に伴って分子・分母が大きくなる可能性が高いので、整数がオーバーフローする可能性を除去できない。

ということで、分子・分母がある一定の数値を超えない特殊な計算を除く、一般的な有理数クラスは現実的ではないと思います。

・実数クラス 

  • 計算によっては、式が簡単になることがあるが、稀なケースであり、式が複雑になるケースが多い。
  • 一般には、計算に伴って式が複雑になる可能性が高く、式の複雑さに比例してデータ構造が複雑になる。
  • データ構造の複雑さに伴って、計算時間が長くなり、メモリ消費量も大きくなるので、実時間で計算が終了しない可能性、メモリ不足になる可能性を除去できない。

ということで、式がある一定の複雑さを超えない特殊な計算を除く、一般的な実数クラスは現実的ではないと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.23%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る