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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C

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

C++

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

Q&A

解決済

6回答

5882閲覧

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

strike1217

総合スコア651

C

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

C++

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

1グッド

0クリップ

投稿2017/03/02 09:36

編集2017/03/03 06:13

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

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

C++

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

省略していますが、√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さんの回答を元に面白いサイトを発見しましたので、載せておきますね。
多倍長演算

fuzzball👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

ベストアンサー

こんにちは。

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

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 10:09

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2017/03/02 10:13

おお!! int型のようにchar型を複数にまとめているのは、ハードウェアレベルなんですか! 「C言語は型を拡張する仕組みを持っていないので、型を自力で作ることはできないです。 C++には型を拡張するclassがあるので可能です。でも、かなりたいへんと思います。」 C言語では出来ないんですね! 分かりました。
strike1217

2017/03/02 10:15

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

2017/03/02 11: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桁ちょっとです。
strike1217

2017/03/03 03:29

では、そのあとの数字は無視しても大丈夫ですか?? 「C++には型を拡張するclassがあるので可能です。」 これについて参考になるサイトなどがあれば教えてもらえないでしょうか??
Chironian

2017/03/03 04:36

> では、そのあとの数字は無視しても大丈夫ですか?? 文脈が分からないのでなんとも言えませんが、有効桁数を超えた部分に意味はないですから、基本的には無視して良いです。 > 「C++には型を拡張するclassがあるので可能です。」 これについて参考になるサイトなどがあれば教えてもらえないでしょうか?? その考え方を解説しているサイトは直ぐには見つかりませんでした。 要するに+や-等の演算子を、拡張した型(class)用に定義することで、その型(class)を通常の計算式の中で普通に使えるようにすることができるのです。 意外にたくさんの演算子とコンストラクタを定義しないといけないので結構手間がかかります。 実際のやりかたについては下記が分かりやすいです。 http://stlalv.la.coocan.jp/Operator.html
strike1217

2017/03/03 06:08

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

2017/03/03 09:08

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

2017/03/03 09:31

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

2017/03/03 09:47

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

2017/03/03 11:45 編集

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

0

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

有効桁数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 02:23

haru666

総合スコア1591

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2017/03/03 03:26

誤差を含めてdouble型は何桁まで扱うことができるのでしょうか?? 2.225074 10^-308 < double の絶対値 < 1.797693 10^+308 これだと300桁くらいまで使えるものということでしょうか??
haru666

2017/03/03 04: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桁程度である、そもそも近似値である、ということです。
strike1217

2017/03/03 06:09

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

2017/03/03 06: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桁の意味なのです。
strike1217

2017/03/03 06:41

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

2017/03/03 08:28

そうですね。 指数部との演算上15桁を超える数値がたまたまびしっと表現できる場合もあると思いますが。 この15桁の数値というのは52bitの仮数部に含まれている値です。 52bitで表現できる値は2^52乗=16桁の途中に終わるぐらいなので、有効桁数15桁がすっぽり収まる形になるわけです。 15桁までの値を最初に定義しているのであれば仮数部にすっぽり値も入りますし、10ずつ割っていっても指数の変動で値をシフトできるので値が正確に表現できるわけです。ただ実際の演算ではもっと汚いビットの内情になっているのでこんな風にはならないと思います。 なので繰り返しになりますが近似値である、というわけです。
strike1217

2017/03/03 08:39

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

0

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

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

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

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

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

投稿2017/03/02 09:57

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2017/03/02 10:00

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

2017/03/02 10:03

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

0

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

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

投稿2017/03/02 10:46

Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2017/03/03 03:27

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

2017/03/03 06:22

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

0

参考情報

...
多倍長演算の実装の基本は、筆算での計算方法です。

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

...

投稿2017/03/03 14:11

katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2017/03/06 16:39

ありがとうございます。
guest

0

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

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

投稿2017/03/03 00:00

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2017/03/03 03:27

やはり難しいんですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問