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

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

ただいまの
回答率

90.32%

  • C

    3997questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • C++

    3781questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

C言語:自分で変数を作る。

解決済

回答 6

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,204

strike1217

score 563

この質問に至った経緯から説明します。

√3を小数計算をするプログラムを作りました。

double x = 1;

for (int i = 1; i <= N; i++) {
    x = ((x*x + a) / (2 * x));
        printf("%d : %02.100lf\n", i, x);
    }


省略していますが、√3 なので、a = 3になっています。
ニュートン法を使って漸化式を作りました。

結果は以下のようになりました。

calc root : 3
1 : 2.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000
2 : 1.75000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000
3 : 1.73214285714285720629845855000894516706466674804687500000000000000000000000000000000000000
00000000000
4 : 1.73205081001472738222446423606015741825103759765625000000000000000000000000000000000000000
00000000000
5 : 1.73205080756887719317660412343684583902359008789062500000000000000000000000000000000000000
00000000000
6 : 1.73205080756887741522120904846815392374992370605468750000000000000000000000000000000000000
00000000000
7 : 1.73205080756887719317660412343684583902359008789062500000000000000000000000000000000000000
00000000000
8 : 1.73205080756887741522120904846815392374992370605468750000000000000000000000000000000000000
00000000000
9 : 1.73205080756887719317660412343684583902359008789062500000000000000000000000000000000000000
00000000000
10 : 1.7320508075688774152212090484681539237499237060546875000000000000000000000000000000000000
000000000000
11 : 1.7320508075688771931766041234368458390235900878906250000000000000000000000000000000000000
000000000000
12 : 1.7320508075688774152212090484681539237499237060546875000000000000000000000000000000000000
000000000000
13 : 1.7320508075688771931766041234368458390235900878906250000000000000000000000000000000000000
000000000000
・・・

結果が繰り返されているので、
「doubleの型に収まる桁の限界がきているのかな??」
と思っているのですが、以下のサイトを見つけました。(わかりやすいです。)
浮動小数点数型と誤差

2.225074 10^-308 < double の絶対値 < 1.797693 10^+308
こうなっていたので、「あれ?? 全然大丈夫そうだけど・・・」
なんで、こんなに小さい桁数で結果が繰り返されているのか不明なのですが・・・

ここでdoubleよりもっと大きい桁を扱える変数を自作する方法を教えて下さい。

以前、以下のような質問をしました。
int型の先頭アドレスについてです。

char型を複数使うことでint型を作り出しているなら、それと同じ原理でオリジナル変数をつくればもっと結果の繰り返しを遅らせることができるのではないかと思っています。

ちなみに、long double を使ってみましたが、結果は同じでした。

ちょっと説明しにくいのですが、
より大きな桁を扱うための変数の作り方を教えてほしいです。
お願いします。

環境は windows 64bit, 64bitコンパイルをしました。

[追記]

Chironianさんの回答を元に面白いサイトを発見しましたので、載せておきますね。
多倍長演算

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 6

checkベストアンサー

+4

こんにちは。

リンク先にも書かれている通り下記のような処理系が多いです。

double 型の精度(有効桁数)は2進数にして 53 (=52+1) 桁であり,10進数では約 15 桁となる.

10進数15桁が有効桁数です。

1.7320508075688774152212090484681539237499237060546875
1.7320508075688771931766041234368458390235900878906250

の一致している桁数は16桁ですので、ほぼ合ってます。
多くの処理系は、IEEE 754に準拠してますので、15.95桁が有効桁数ですから、妥当な結果と思います。

ちなみに、long double を使ってみましたが、結果は同じでした。

Visual C++はlong doubleとdoubleは同じ倍精度(binary64)だったと思います。
MinGW(gcc)なら、long doubleは拡張精度形式(80ビット使います)なのでもう少し精度が上がると思います。

char型を複数使うことでint型を作り出しているなら、それと同じ原理でオリジナル変数をつくればもっと結果の繰り返しを遅らせることができるのではないかと思っています。

それをやっているのはハードウェア(CPU)です。
なので、CPUが回路的に実装していないサイズを使うのは容易ではありません。

とは言え、ソフトウェアで拡張することは可能ですね。
C言語は型を拡張する仕組みを持っていないので、型を自力で作ることはできないです。

C++には型を拡張するclassがあるので可能です。でも、かなりたいへんと思います。
任意精度で実数演算を行うライブラリを探した方が速いです。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/03/02 19:13

    おお!!
    int型のようにchar型を複数にまとめているのは、ハードウェアレベルなんですか!

    「C言語は型を拡張する仕組みを持っていないので、型を自力で作ることはできないです。
    C++には型を拡張するclassがあるので可能です。でも、かなりたいへんと思います。」
    C言語では出来ないんですね!
    分かりました。

    キャンセル

  • 2017/03/02 19:15

    1.732050807568877 この辺りまでが有効数字15桁 ということですね!
    そのあとの数字はなんですか?
    紛らわしいですね・・・

    キャンセル

  • 2017/03/02 20:57

    > そのあとの数字はなんですか?

    小数点以下の数値の場合、2で割り切れないので無駄に桁が増えます。
    1ビットで1/2を表現できますね。0.5です。
    2ビットなら1/4で0.25です。
    3ビットなら1/8で0.125、4ビットなら1/16で0.0625ですね。
    4ビットは10進数1桁ちょっとです。

    キャンセル

  • 2017/03/03 12:29

    では、そのあとの数字は無視しても大丈夫ですか??

    「C++には型を拡張するclassがあるので可能です。」
    これについて参考になるサイトなどがあれば教えてもらえないでしょうか??

    キャンセル

  • 2017/03/03 13:36

    > では、そのあとの数字は無視しても大丈夫ですか??

    文脈が分からないのでなんとも言えませんが、有効桁数を超えた部分に意味はないですから、基本的には無視して良いです。

    > 「C++には型を拡張するclassがあるので可能です。」
    これについて参考になるサイトなどがあれば教えてもらえないでしょうか??

    その考え方を解説しているサイトは直ぐには見つかりませんでした。
    要するに+や-等の演算子を、拡張した型(class)用に定義することで、その型(class)を通常の計算式の中で普通に使えるようにすることができるのです。
    意外にたくさんの演算子とコンストラクタを定義しないといけないので結構手間がかかります。

    実際のやりかたについては下記が分かりやすいです。
    http://stlalv.la.coocan.jp/Operator.html

    キャンセル

  • 2017/03/03 15:08

    ああ〜〜
    演算子のオーバーロードですね。
    わかりました。
    ありがとうございます。

    キャンセル

  • 2017/03/03 18:08

    すいません。
    1.7320508075688771931766041234368458390235900878906250000000000000000000000000000000000000000000000000
    結果に出てきた、これらの数字はすべて仮数部ですか?
    15桁を超えた数字というのは、2で割り切れない余った数字ですよね。

    printf()の表示では指数部までは表示されていませんよね??

    キャンセル

  • 2017/03/03 18:31

    仮数部だけなら整数だけですよ。
    指数部も含めて10進数へ変換されているから小数点以下がある形式で出力されてます。

    キャンセル

  • 2017/03/03 18:47

    あ!
    そういうことですか!
    分かりました。

    キャンセル

  • 2017/03/03 20:45 編集

    演算子のオーバーロードで実現できることがわかったので、ベストアンサーにさせてもらいますね。
    double型のフォーマットについては再度質問するかもしれません。

    キャンセル

+2

C言語のdoubleは多くの場合、浮動小数点数になっています。大きい数字を「2.3×10^27」のように書くことがありますが、そんな感じで、「1.11011×2^15」のような形で記録されています。そのため、精度の絶対的な大きさは値の大きさによって変化します(10進法で16桁程度)。

一般によく使われるIEEE 754の倍精度64ビットの場合、

  • 符号…1ビット
  • 仮数部…52ビット
  • 指数部…11ビット

というように配分されています。

このような複雑な構造なので、さらに大きな数を自分で作ろうとしても、正しい演算ルーチンを作るだけで一苦労です。GMPなど、既存の演算ルーチンを使うのが手っ取り早いです。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/03/02 19:00

    「精度の絶対的な大きさは値の大きさによって変化します」
    そうなんですか!

    キャンセル

  • 2017/03/02 19:03

    GMPについて調べてみますね。

    キャンセル

+2

小さい桁数で値が繰り返されるのはこの漸化式では自然なことだと思います。
無限に繰り返すことで平方根の正確な値に限りなく近づいていく式、なので、元々無理数の小数部をより小さい方に向かって計算し続ける式なわけで、結果はこれで良いと思います。

有効桁数15以下の値が変わり続けるのはなぜなのか?
それは、有効桁数15桁というのは、15桁までは「妥当な近似値」で表せる、という意味だからです。
1.732050807568877より下の部分、16桁以降の数値は誤差部分なんです。

倍精度少数は元々全ての値を適切に表現できるわけではありません。
このサイトなんかが分かりやすいです。

このため、値が範囲内であっても、倍精度少数が表してる値は全て「近似値」なんだと思ってください。
人間の目から見たら同じに見える値でも倍精度少数のbit配列は異なっていれば等価と評価できない不安定なものです。

Cよりも上位の言語では、小数点以下もintのように整数値で表したdecimalという型があります。
これで同じ計算をしてみると以下のようにアウトプットされます。

1 : 2
2 : 1.75
3 : 1.7321428571428571428571428571
4 : 1.732050810014727540500736377
5 : 1.7320508075688772952543539461
6 : 1.7320508075688772935274463415
7 : 1.7320508075688772935274463415
...

以降全て結果は1.7320508075688772935274463415になります。
Wikipediaに乗ってるルート3の値は以下のようになっていますが、計算できてるところまでは一致していますね。

1.732050807568877293527446341505872366942805253

C#のdecimalは精度が固定なのでこれ以上の計算はできないのですが、decimalだって元々繰り返し処理の中で循環小数が現れる時点で誤差を含んでいます。
double型を使うと値が変わり続けているのは、精度が足りないため誤差が大きくなり、その誤差の中で結果がブレてしまうために値が変わり続けているように見えるわけです。

大手の作る複雑な計算式の演算を行ってくれるものがどうやって解決しているかまでは分かりませんが、可能な限り式を展開して2進数的に無理な計算が行われる回数を減らすことによって精度を上げているとか、そんなことをしているんじゃないかと思います。

丸めても現実的には困らない精度までは結果を出すことができるのですが、コンピューターでも無理数や循環小数の計算というのは存外難しいです。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/03/03 12:26

    誤差を含めてdouble型は何桁まで扱うことができるのでしょうか??

    2.225074 10^-308 < double の絶対値 < 1.797693 10^+308
    これだと300桁くらいまで使えるものということでしょうか??

    キャンセル

  • 2017/03/03 13:15

    有効桁数は15桁と書いてある通りです。
    実際のところ小数点1桁の数値だろうと誤差を丸めて表示して表しているに過ぎません。
    ツールとして使える精度だけど、絶対的なものではないんです。

    それは文言通り「絶対値」です。
    元々int型と違って言葉通りの数値ではなく、計算結果に基づくものなので1つ1つの値の差は「1」単位ではないわけです。そのためにC#のdoubleのMINは2.225074 10^-308だし、MAXは1.797693 10^+308だけど、この間のすべての数字を隙間なく表現できるという意味ではありません。

    その程度の理解でdouble型は十分に利用できるものだと個人的には思いますよ。
    何故「信頼できない」型なのかもうちょっと考えてみてください。

    Wikipediaを見てみてください。
    https://ja.wikipedia.org/wiki/%E5%80%8D%E7%B2%BE%E5%BA%A6%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%95%B0

    1bit動かした時の値の変動は
    1.0000000000000002
    1.0000000000000004
    になっていますよね

    こちらのサイトではdouble型との独特な付き合い方について述べていますが
    http://awakia-n.hatenablog.com/entry/20071117/1195292892

    最初に言った通り、double型を利用する上での重要な理解とは、良くて15桁程度である、そもそも近似値である、ということです。

    キャンセル

  • 2017/03/03 15:09

    わかりました。
    有効数字15桁以外の数字は普通に無視して大丈夫ですね。
    ありがとうございます。

    キャンセル

  • 2017/03/03 15:20

    あ、あと、小数点15桁って意味ではないです。分かりにくくてごめんね。
    例えば、double x = 1を10で割ること30回してみてください。

    01 0.100000000000000000000000000000
    02 0.010000000000000000000000000000
    03 0.001000000000000000000000000000
    04 0.000100000000000000000000000000
    05 0.000010000000000000000000000000
    06 0.000001000000000000000000000000
    07 0.000000100000000000000000000000
    08 0.000000010000000000000000000000
    09 0.000000001000000000000000000000
    10 0.000000000100000000000000000000
    11 0.000000000010000000000000000000
    12 0.000000000001000000000000000000
    13 0.000000000000100000000000000000
    14 0.000000000000010000000000000000
    15 0.000000000000001000000000000000
    16 0.000000000000000100000000000000
    17 0.000000000000000010000000000000
    18 0.000000000000000001000000000000
    19 0.000000000000000000100000000000
    20 0.000000000000000000010000000000
    21 0.000000000000000000001000000000
    22 0.000000000000000000000100000000
    23 0.000000000000000000000010000000
    24 0.000000000000000000000001000000
    25 0.000000000000000000000000100000
    26 0.000000000000000000000000010000
    27 0.000000000000000000000000001000
    28 0.000000000000000000000000000100
    29 0.000000000000000000000000000010
    30 0.000000000000000000000000000001
    ちゃんと印字できます。

    以下のコードはまともに動きます。
    static void Main(string[] args)
    {
    double x = 123456789012345;

    for (int i = 1; i <= 30; i++)
    {
    Console.WriteLine($"{i:00} {x:0000000000000000.000000000000000000000000000000}");
    x = x / 10;
    }

    Console.ReadKey();
    }

    15桁の数値を10で割っていきながら表示しても問題はおきません。
    でもxの桁を1つ増やしてみてください。
    例えば、123456789012345を1234567890123456にしてみましょう。

    すると、値は丸められて1234567890123460として扱われてしまいます。
    これが「浮動小数点」と15桁の意味なのです。

    キャンセル

  • 2017/03/03 15:41

    えーーと・・・
    15桁を超えると四捨五入されるということでしょうか?

    キャンセル

  • 2017/03/03 17:28

    そうですね。
    指数部との演算上15桁を超える数値がたまたまびしっと表現できる場合もあると思いますが。

    この15桁の数値というのは52bitの仮数部に含まれている値です。
    52bitで表現できる値は2^52乗=16桁の途中に終わるぐらいなので、有効桁数15桁がすっぽり収まる形になるわけです。

    15桁までの値を最初に定義しているのであれば仮数部にすっぽり値も入りますし、10ずつ割っていっても指数の変動で値をシフトできるので値が正確に表現できるわけです。ただ実際の演算ではもっと汚いビットの内情になっているのでこんな風にはならないと思います。

    なので繰り返しになりますが近似値である、というわけです。

    キャンセル

  • 2017/03/03 17:39

    分かりました。
    ありがとうございます。

    キャンセル

+1

見つけてきただけで一切の検証はしていません。
bigfloat

BigFloat の作成は研究している人も多いので、自分で作るよりも探した方が早いかもしれません。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/03/03 12:27

    調べてみます。
    ありがとうございます。

    キャンセル

  • 2017/03/03 15:22

    あなたのお作りになろうとしている、長い桁の小数を表せるデータ型はよく BigFloat と呼ばれます。リンク先は C での実装です。構造体を使って表現しています。ソースをご覧になれば、自作される場合でも役立つと思います。

    キャンセル

  • 2017/03/03 15:36

    分かりました。

    キャンセル

0

引用テキストここでdoubleよりもっと大きい桁を扱える変数を自作する方法を教えて下さい。

doubleより大きい変数(もしくはシステムが許す最大変数)を設けるのは困難ではと思います。単に演算結果を求めたいなら変数を複数使って疑似的に行うことはできると思います。それより、演算結果を入れる変数がないとどうしようもないですが。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/03/03 12:27

    やはり難しいんですね。

    キャンセル

0

参考情報

...
多倍長演算の実装の基本は、筆算での計算方法です。 
一桁同士の足し算、引き算、掛け算(九九の表) があれば、筆算でいくらでも大きな数での計算が 紙の上で行えますよね。 
その方法を 配列をつかって行うのです。 
...

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/03/07 01:39

    ありがとうございます。

    キャンセル

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

  • C

    3997questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • C++

    3781questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。