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

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

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

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

Q&A

3回答

1121閲覧

C言語でf(x) = e^x-1の高精度な数値を求めたいです

raiboz1115

総合スコア4

C

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

0グッド

1クリップ

投稿2022/02/09 09:45

C言語でf(x) = e^x-1の高精度な数値を求めたいですのですが、10^-15の位からどうしても値が合いません。
以下のfor文の書き方で積み残しは対処できたと思っています。
自分は桁落ちが対処できていないのかなと思います。四倍精度小数点型を使用すれば対処できるのかなとは思うのですが、使用方法がわかりません。
以下に実行結果を掲載させていただきます
【実行結果(項数100の時)】
項数 = 100
f(x) =  0.0000100000500001666682125655433166500075
解析値 =0.0000100000500000696490587870357558131218
誤差 =  0.0000000000000000970191537785075608368857

C

1#include<stdio.h> 2#include<math.h> 3 4double kaizyou(int); 5 6int main(void){ 7 int N; 8 int n; // 項数 9 int k; // 項数カウント 10double x; // e^xのx 11double xk; // x^n/n! 12double e; // テイラー展開の部分和 13double fx;//解析値 14double er; // exp(x)との差 15 16 printf("N = "); 17 scanf("%d", &N); 18 x = pow(10, -5); 19 n = pow(10, N); 20 21 printf("項数 = %d\n", n); 22 23 e = 0.0; 24 xk = 1.0; 25 26 for(k = n ; k >= 1 ; --k){ 27 e += kaizyou(k); 28 } 29 30 fx = exp(x)-1; 31 er = fabs(e-fx); 32 printf("f(x) =\t%.40f\n", e); 33 printf("解析値 =%.40f\n", fx); 34 printf("誤差 =\t%.40f\n", er); 35 36 return 0; 37} 38 39double kaizyou(int n){ 40 int i; 41 long double x = 1, d = pow(10, -5); 42 43 for(i = n ; i >= 1 ; --i){ 44 x *= d/(double)i; 45 } 46 47 return x; 48} 49

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

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

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

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

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

guest

回答3

0

他の方もおっしゃっている通り、本来 15 桁ほどあるはずの double の有効桁数が exp(x) - 1 の部分で 10 桁ほどに減ってしまうのが原因です。
実は exp(x) - 1 を (桁落ちさせずに精度よく) 求める専用の関数があります: https://en.cppreference.com/w/c/numeric/math/expm1
fx = exp(x)-1; の行を fx = expm1(x); に書き換えれば e == fx になることが確かめられます。

とはいえ、有効数字 15 桁 (小数点以下 20 桁) を超えて完全に一致しているのは偶然でしかないので、もし有効数字を増やしたいのであれば

  • コンパイラの独自拡張の 4 倍精度を使う
  • 多倍長実数のライブラリ (mpfr またはそれの boost ラッパ) を使う

のどちらかですね。

投稿2022/02/15 04:25

selpo

総合スコア41

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

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

0

どれだけの精度が必要なのかわかりませんが、多倍長浮動小数点数ライブラリを使う手もあります。

ちょっと調べてみたところ、Boostにもあるみたいですね。URLを貼っておきます。

多倍長浮動小数点数

投稿2022/02/15 02:44

JunSuzukiJapan

総合スコア310

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

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

0

まず前提としてdoubleの精度は十進数表現で約15桁というのはご認識されていると思います。
exp(1e-5)は約15桁の精度を持っていますが、それは先頭の整数部の1から15桁です。
ということで、

解析値 =0.0000100000500000696490587870357558131218

とお書きの数値のうち、有効なのは、0.00001000005000だけです。
ほぼ同じ値同士の引き算で、桁落ち(正しい意味)が発生し、有効精度が減ってしまいました。

任意精度計算ツールのbcを使うと、

sh

1$ echo 'scale=50;e(0.00001)-1' | bc -l 2.00001000005000016666708333416666805555753968501984

なので、お書きのプログラムで計算された値は有効桁15桁で正しいようです。

追記

gccだとデフォルトではlong doubleは80bit拡張倍精度(19桁)のようなので(16バイトなのに)、オプションで4倍精度(34桁)にします。
printfは4倍精度に対応していないようです。また、浮動小数点関数は倍精度なので使えません。

sh

1gcc -mlong-double-128 -lm -lquadmath -o foo foo.c

C

1#include <stdio.h> 2#include <math.h> 3#include <quadmath.h> 4 5typedef long double FLOAT; 6FLOAT kaizyou(int); 7 8int main(void){ 9 int N; 10 int n; // 項数 11 int k; // 項数カウント 12 FLOAT e; // テイラー展開の部分和 13 FLOAT er; // exp(x)との差 14 FLOAT fx = .00001000005000016666708333416666805555753968Q; 15 char buf[100]; 16 17 printf("N = "); 18 scanf("%d", &N); 19 n = pow(10, N); 20 printf("項数 = %d\n", n); 21 e = 0.0; 22 for(k = n ; k >= 1 ; --k){ 23 e += kaizyou(k); 24 } 25 er = e-fx; 26 if(er<0) er = -er; 27 quadmath_snprintf (buf, sizeof buf, "%.40Qf", e); 28 printf("f(x) =\t%s\n", buf); 29 quadmath_snprintf (buf, sizeof buf, "%.40Qf", fx); 30 printf("解析値=\t%s\n", buf); 31 quadmath_snprintf (buf, sizeof buf, "%.40Qf", er); 32 printf("誤差=\t%s\n", buf); 33 return 0; 34} 35 36FLOAT kaizyou(int n){ 37 int i; 38 FLOAT x = 1, d = 0.00001Q; 39 40 for(i = n ; i >= 1 ; --i){ 41 x *= d/(FLOAT)i; 42 } 43 return x; 44}

投稿2022/02/09 10:50

編集2022/02/09 13:36
otn

総合スコア84555

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

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

otn

2022/02/09 10:54

scale=50と書いてますが、e()を計算する際の内部の各計算が50桁で行われると言うことだと思いますので、e()の結果が50桁の精度を持っているわけではないでしょうね。
raiboz1115

2022/02/09 11:52

回答ありがとうございます。とても分かりやすくて助かりました! 有効桁を15桁以上にするにはどうすればよいでしょうか?
otn

2022/02/09 13:11

doubleをlong doubleにするのでしょうか。 4倍精度やったことなかったので、やってみました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問