🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C

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

浮動小数点

浮動小数点は、コンピュータが数値を扱う際に実数を表現する方法のひとつです。 数値を、それぞれの桁の値が並んでいる仮数部と、小数点の場所を示す指数部で表します。

Q&A

2回答

4294閲覧

ネイピア数eの小数点以下の桁数をできるだけ多く求めるにはどうすればよいですか?

boxod-9116

総合スコア4

C

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

浮動小数点

浮動小数点は、コンピュータが数値を扱う際に実数を表現する方法のひとつです。 数値を、それぞれの桁の値が並んでいる仮数部と、小数点の場所を示す指数部で表します。

0グッド

2クリップ

投稿2019/12/25 06:01

編集2019/12/25 23:59

C

1コード 2void kaizyou(int *x){ //階乗したい数 x! Lはマクロで宣言済み 3 int ans[L] = {0}; 4 int copy[L] = {0}; 5 int n,g,k,carry,t; //n:k倍する桁 g:最高位桁 6 t = *x; //階乗したい数 t! = x! 7 ans[1] = 1; 8 g = 1; 9 carry = 0; 10 11 for(k = 2; k <= t; k++){ 12 for(i = 0; i < L; i++){ 13 copy[i] = ans[i]; 14 } 15 for(n = 1; n <= g+1; n++){ 16 ans[n] = ans[n]*k + carry; 17 carry = ans[n]/10; 18 if(carry != 0){ 19 ans[n] = ans[n]%10; 20 if((n+1) > g) 21 g = n+1; 22 } 23 } 24 } 25} 26```### 前提・実現したいこと 27初投稿です。プログラミングを一ヶ月ほどになります。 28C言語において、ネイピア数eの小数点以下の桁数をできるだけ多く求めて表示する関数を作ろうと考えています。 29追記: 30桁数をできるだけ多くとは、double以上の精度かつ、小数点以下の桁数が例えば500桁まで求めるこができることを指します。 31以下にソースコードの一部を載せておきます。 32### 発生している問題・エラーメッセージ 33

1/n!のn!が配列で表現されているとき、1/n!をどのようにして計算するかを教えていただきたいです。具体的にはn! = A[3]A[2]A[1]であるとき、1/n! = 1 / A[3]A[2]A[1] の計算の仕方を知りたいです。例えば、これを近似する式が存在していたり、式をうまく分割することができたりするといった感じに。(このままでは桁数が多くなると計算ができなくなってしまうので)
n!が配列で表現されているとは、n!が配列A[N]で表されるとき、n!=A[N-1]A[N-2]・・・A[0]となっていることを指します。

### 試したこと 再帰関数を利用した階乗を計算するものだと、限界があるため、配列を用いて階乗を求めました。 ### 補足情報(FW/ツールのバージョンなど) gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39)

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

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

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

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

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

y_waiwai

2019/12/25 06:02

このままではコードが読みづらいので、質問を編集し、<code>ボタンを押し、出てくる’’’の枠の中にコードを貼り付けてください
boxod-9116

2019/12/25 06:11

ご指摘ありがとうございます。ソースコードを貼り直しました。
退会済みユーザー

退会済みユーザー

2019/12/25 08:50

何をしたいのかちょっと分かりません。n!が配列で表現されているとはどういう状態でしょうか。あとうるさいかもしれませんがインデントは可読性に直結するので正しく入れた方が良いと思います。多分}が足りてないです。
otn

2019/12/25 09:23

> 1/n!のn!が配列で表現されているとき、1/n!をどのようにして計算するかを教えていただきたいです。 の質問に答えると、「n!がわかっていて、1/n!を求めるには、1をn!で除算します」ということになりますが、おそらくそれが聞きたいことでは無いですよね?質問を書き損ねていると思います。書き直しましょう。
2KOH

2019/12/25 10:19

倍精度 (double) 以上の精度で計算したい、という意味ですか?
boxod-9116

2019/12/26 00:00

様々なご指摘ありがとうございます。勉強になります。
guest

回答2

0

まず、任意精度演算と呼ばれるクラスを作ります
多くてもint128ぐらいしかないので、それ以上の桁数を計算する場合は
自作する必要があります

配列を作って、1つに1桁ずつ入れていき、繰り上がりを実装する感じですね
やり方は簡単で、小学校の頃に筆算を習ったと思います
あの仕組みをそのままプログラムとして書くだけです

そうして、無限桁長の整数クラスができたとします

1/k! を無限に足すとして、小数点以下500桁を計算する場合、
log k! > 500 となるkを求め、分母をk!として分子を整数で計算します

あとは、これも筆算の要領で割っていけば、求めることができます

投稿2019/12/26 01:15

izmktr

総合スコア2856

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

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

boxod-9116

2019/12/28 01:23

回答ありがとうございます。 もう一度じっくり考えてみます。
guest

0

ネイピア数eの小数点以下の桁数をできるだけ多く求めて表示する
小数点以下の桁数が例えば500桁まで求めるこができること

多倍長計算として比較的簡単に実現できます。私の手元では数十行程度のプログラムで小数点以下1000桁を求めることができました。

1/n!のn!が配列で表現されているとき、
1/n!をどのようにして計算するかを教えていただきたいです。
具体的にはn! = A[3]A[2]A[1]であるとき、
1/n! = 1 / A[3]A[2]A[1] の計算の仕方を知りたいです。

「n! = A[3]A[2]A[1]である」の意味がわかりませんが、それはともかく・・・

ネイピア数
階乗を使うのは、このページの「微分積分学の基本的な関数を使った定義」式を使おうということだと思います。その式をそのままプログラムすれば次のようなコードになるでしょう。

C

1 double napier = 1.0; 2 for (int n = 1; 十分な回数; n++) { 3 napier += 1.0 / factrial(n); 4 } 5 printf("napier = %g\n", napier);

factrial(i) は i の階乗を計算する関数を想定したものです。しかしプログラムを作るには、階乗そのものを計算しないほうが良いと考えます。
上のプログラムは次のように書き変えることができます。

C

1#include <stdio.h> 2#include <float.h> 3int main(void) 4{ 5 double napier = 1.0, differ = 1.0; 6 for (int n = 1; differ > DBL_EPSILON; n++) { 7 differ /= n; 8 napier += differ; 9 } 10 printf("napier = %.20g\n", napier);

数学的な意味は同じですが、こう書き変えれば

  • 直接、階乗を計算しないですむ
  • 多倍長計算にも向いている

ことから、計算機的に良い方法だと考えています。まずは、

  • このコードはどう書き変えたのか
  • 実際に動作結果を見る(桁数の少ない近似値が表示されるはず)

をご確認ください。その上で、この計算方法をお伝えすべきかどうかを確認する必要があると思っています。なぜならお気づきと思いますが、要件と思われる「階乗の配列」を使わない方法だからです。
なお、階乗の配列を使い500桁計算する方法であれば、私の手には負えませんので、他の方におたずねください。


気づいたら反応が無いまま日が経ちましたが、せっかくなのでコードをアップします。

この問題は多倍長演算としては初歩的な部類に入ると思います。私でもゼロから書けた位。
多倍長演算はある数を配列で表すことで多くの桁数を保持しますが、アルゴリズム等によって配列の使い方はいくつかあるようです。
今回は、配列の一要素に0〜99999の値を、即ち5桁分を格納することにしました。たとえば int napier[200]; とした場合、10 進数で最大 1000 桁を保持します(ただし、最下位2桁位は誤差が残るので、小数点以下 1000 桁を求める場合 1005 桁まで求める)。

こうした理由はふたつあります。

  • 表示が簡単、"%05d"で済む。上記 wikipedia に挙げられた、5桁ずつの表示に合わせるのに都合がよい。
  • 私のやり方なら小さい整数で割り算ができればよく、筆算の要領で簡単にプログラムできる。

今回作った足し算と割り算を解説します。どちらもそんなに難しくありません。

add() 関数は多倍長 + 多倍長の足し算です。2つの数の、下の桁から、同じ桁同士を足していきます。100000 を超えた場合、99999 以下を残し、次の桁に桁上がり(carry)を送る・・・筆算のやり方です。

div() 関数は多倍長 ÷ 整数の割り算です。今度は上の桁から整数で割り、余りを次の桁への繰り下がり(borrow)として割り算を繰り返します。これも筆算の応用です。今回は割る数が比較的小さい整数に収まるので、簡単なコードで済みます。

differ[] 配列を除数 divisor で割った結果、配列全てが0になった時点で計算は終了です。小数点以下 500 桁を求める場合は 256 で割った時、1000 桁を求める場合は、452 で割った時、それぞれ終了しました。
このことは 256 の階乗も 500 桁程度になることを意味します。質問の

1/n! = 1 / A[3]A[2]A[1] の計算の仕方を知りたい

は多倍長 ÷ 多倍長の割り算、即ち多倍長整数での割り算を求めているようにも受け取れます。しかし、階乗を求めるのも、割り算するのも、大変です。計算の負荷をムダに重くするばかりで、もはや挑戦しようという気になりません。

C

1#include <stdio.h> 2#include <stdbool.h> 3 4#ifdef DEBUG 5# define DIGITS (40) // with -D DEBUG 6#else 7# define DIGITS (1000) // 小数点以下に桁数-- 8#endif 9#define ARRSIZE ((DIGITS / 5) + 2) // 必要な配列サイズ 10#define UNIT (100000) // 配列の一要素は00000〜99999を格納 11 12void display(char *msg, int xx[]); // 配列 xx[] を表示 13void add(int sum[], int yy[]); // sum += yy 14bool div(int ddd[], int divisor); // ddd /= divisor 15 16int main(void) 17{ 18 int napier[ARRSIZE] = {1}; // napier = 1.0000... 19 int differ[ARRSIZE] = {1}; // differ = 1.0000... 20 21 for (int n = 1; ; n++) { 22 bool zero = div(differ, n); // differ /= n 23 if (zero) { 24 printf("DEBUG: exit on %d as divisor.\n", n); 25 break; // 商が0なら終了 26 } 27 add(napier, differ); // napier += differ 28 29#ifdef DEBUG // 途中経過を表示してみる 30 printf("DEBUG: divided by %d\n", n); 31 display("DEBUG: differ", differ); 32 display("DEBUG: napier", napier); 33#endif 34 } 35 36 display(" napier", napier); // 最終結果 37 return 0; 38} 39 40void display(char *msg, int xx[]) // 配列 x[] を表示 41{ 42 printf("%s = %d.", msg, xx[0]); // 整数部と小数点 43 for (int k = 1; k < ARRSIZE; k++) 44 printf("%05d ", xx[k]); // 5桁ずつ表示 45 printf("\n"); 46} 47 48void add(int sum[], int yy[]) // sum += y 49{ 50 int carry = 0; // 桁上がり 51 for (int k = ARRSIZE - 1; k >= 0; --k) { 52 // 同じ桁位置の2数+下位からの繰上がり 53 int num = sum[k] + yy[k] + carry; 54 55 if (num >= UNIT) { // 桁あふれ? 56 num -= UNIT; // 99999 以下を残す 57 carry = 1; // 桁上がりあり 58 } else { 59 carry = 0; // 桁上がりなし 60 } 61 sum[k] = num; // その桁の足し算結果 62 } 63} 64 65bool div(int ddd[], int divisor) // ddd /= divisor 66{ 67 bool allzero = true; // 商の全ての桁が0か? 68 int borrow = 0; // 次の位置に送る余り=桁下がり 69 70 for (int k = 0; k < ARRSIZE; k++) { 71 // 「上の桁から桁下がり * 100000 + この桁位置の値」を割る 72 int num = borrow * UNIT + ddd[k]; 73 74 borrow = num % divisor; // 余りを次の位置に送る 75 ddd[k] = num / divisor; // その位置の商 76 if (ddd[k] > 0) allzero = false; // 結果は0じゃない 77 } 78 return allzero; // 商が0になったら true を返す 79} 80

投稿2020/01/01 14:50

編集2020/01/17 03:13
rubato6809

総合スコア1382

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問