浮動小数点の加減算による誤差蓄積について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
celica

浮動小数点の加減算による誤差蓄積について

#1

投稿記事 by celica » 5年前

件名の内容で質問させて頂きます。
100点の浮動小数点データの平均値を計算するプログラムを作成しましたが、
繰り返し演算を行うことで平均結果が徐々に増加する現象が発生してしまいました。
このプログラムでは積算結果がリセットされないため、浮動小数点同士の加減算による情報落ちが蓄積されることが原因だと思いますが、平均結果が元データの範囲を超えて徐々に増加する原因について理解出来ずにいます。原因が分かる方がいらっしゃいましたらご教授いただけないでしょうか。
※instDataのランダム値は正規分布ではありません。

コード:

// 以下、グローバル変数で定義
// unsigned int avgPtr
// float avgData[100]
// float avgSumData

// 100ms毎に計算
void GetAvgResult(void)
{
	float instData;		// 最新データ(99.9~100.1の範囲をランダムに変動)
	float avgResult;		// 平均結果
	
	avgData[avgPtr] = instData;	// 最新データをバッファに格納
	avgSumData += instData;		// 平均用SUM値に最新データ加算
	if(avgPtr >= 99){			// バッファのリングバッファ処理
		avgPtr = 0;
	}
	else{
		avgPtr++;
	}
	avgSumData -= avgData[avgPtr];// もっとも古いデータを減算(FIFO)
	avgResult = avgSumData / 100;
}

結城紬
記事: 42
登録日時: 6年前

Re: 浮動小数点の加減算による誤差蓄積について

#2

投稿記事 by 結城紬 » 5年前

celicaさん、こんにちは。
「徐々に増加する」とは、何件あたりどのくらいで、どこまで増加するのですか?
例えば、データが均一ではなく、大きい方に偏っていれば、徐々に増加する場合もありますが、それとは違いますか?

celica

Re: 浮動小数点の加減算による誤差蓄積について

#3

投稿記事 by celica » 5年前

ご返信頂き、有難うございます。
実際のプログラムはもう少し複雑で、記載させて頂いたプログラムは簡略化したものになります。
そのため増加量について定量的に表せないのですが、
イメージとしては1演算当たりのズレ量は微小で、長時間演算(繰り返し演算)するとズレが蓄積されていき、
最終的には演算結果が元々のランダムデータの最大値100.1を超えて増加していくようなイメージです。
例えば、
加算時(avgSumData += instData)の情報落ちが必ず切り捨て(avgSumDataが真値より大きめになる)
減算時(avgSumData -= avgData[avgPtr])の情報落ちが四捨五入(avgSumDataが真値より大きくなったり、小さくなったりする)
という処理が行われていると徐々に増加すると考えましたが、
float型の有効数字桁以外の処理がどうなっているのかが分からず、
このような現象が起こり得るのかどうか、というのが疑問点になります。

かずま

Re: 浮動小数点の加減算による誤差蓄積について

#4

投稿記事 by かずま » 5年前

その簡略化したプログラムは間違っています。

入力データが 100個に達するまでは、グローバル変数 avgData
に入っている 0 をデータとして扱っているので、100 で割った
平均値は小さなものとなります。
入力データが 100個以上あって、最新の 100個の平均を求めるの
であれば、それは問題ではありません。
最新の 100個のデータを計算対象にしているつもりなのに、その
100個のうちの先頭の 1個を計算対象から外して平均を求めてい
ます。平均を求めたあとで、先頭の 1個を引いてください。

コード:

#include <stdio.h>   // printf
#include <stdlib.h>  // rand, srand
#include <time.h>    // time

#define N 100 
#define TEST 1000

unsigned int avgPtr;
float avgData[N];
float avgSumData;

float getData(void)
{
	return (rand() % (100100 - 99900 + 1) + 99900) / 1000.0;
}

// 100ms毎に計算
void GetAvgResult(void)
{
	float instData = getData();     // 99.9~100.1
	float avgResult;                // 平均結果
	
	printf("avgData[%3d] = %7.3f", avgPtr, instData);
	avgData[avgPtr] = instData;     // 最新データをバッファに格納
	avgSumData += instData;         // 平均用SUM値に最新データ加算
	if (++avgPtr >= N) avgPtr = 0;  // バッファのリングバッファ処理
	avgResult = avgSumData / N;
	avgSumData -= avgData[avgPtr];  // もっとも古いデータを減算(FIFO)
	printf("   avgResult = %7.3f\n", avgResult);
}

int main(void)
{
	srand(time(0));
	for (int i = 0; i < TEST; i++)
		GetAvgResult();
}
テスト回数(TEST)を 10000 にしても 100000 にしても
質問にある現象は再現しません。

現象が再現する簡略化プログラムを提示してください。

celica

Re: 浮動小数点の加減算による誤差蓄積について

#5

投稿記事 by celica » 5年前

ご返信有難うございます。
遅くなりましたが、再現する簡易プログラムを以下に記述します。

コード:

 
// unsigned int avgPtr
// float avgData[100]  各要素に初期値:100000を格納済み
// float avgSumData  初期値:10000000を格納済み
static void GetAvgResult(void)
{
	float instData;	// 最新データ
	float avgResult;	// 平均結果
	unsigned int calcData;
	
	// 最新データが99999.5~100000.5の範囲をランダムに変動(rand関数が正規分布でないのは承知)
	calcData = 99999500 + (rand() % 1000);
	instData = (float)calcData / 1000.0f;
	avgData[avgPtr] = instData;	// 最新データをバッファに格納
	avgSumData += instData;		// 平均用SUM値に最新データ加算
	if(avgPtr >= 99){			// バッファのリングバッファ処理
		avgPtr = 0;
	}
	else{
		avgPtr++;
	}
	avgSumData -= avgData[avgPtr];// もっとも古いデータを減算(FIFO)
	avgResult = avgSumData / 100;
	// avgResultが99999.5未満の数字になり、徐々に減っていく現象が発生
}

かずま

Re: 浮動小数点の加減算による誤差蓄積について

#6

投稿記事 by かずま » 5年前

celica さんが書きました:
5年前
遅くなりましたが、再現する簡易プログラムを以下に記述します。

コード:

 
	avgSumData -= avgData[avgPtr];// もっとも古いデータを減算(FIFO)
	avgResult = avgSumData / 100;
}
既に指摘しました。
もっとも古いデータを減算したら、データは 99個しかありません。
それを 100 で割ったら、結果が小さくなるのは当然です。
次のように、割ってから引いてください。

コード:

	avgResult = avgSumData / 100;
	avgSumData -= avgData[avgPtr];// もっとも古いデータを減算(FIFO)
celica さんが書きました:
5年前

コード:

 
	// avgResultが99999.5未満の数字になり、徐々に減っていく現象が発生
}
最初の質問の現象と違いますね。
celica さんが書きました:
5年前
100点の浮動小数点データの平均値を計算するプログラムを作成しましたが、
繰り返し演算を行うことで平均結果が徐々に増加する現象が発生してしまいました。

かずま

Re: 浮動小数点の加減算による誤差蓄積について

#7

投稿記事 by かずま » 5年前

ひとつ言っておきます。
float は 10進で約 7桁の制度しかありません。
したがって、100個の合計値 avgSumData が 100000000.0
付近の値になると、そこから 100000.0 あたりの入力データを
引いて足しても、小数点以下は桁落ちにより変化しません。

float を double にすることをお勧めします。
double だと約16桁の精度があります。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

unsigned int avgPtr;
double avgData[100];  // 各要素に初期値:100000を格納済み
double avgSumData;    // 初期値:10000000を格納済み

static void GetAvgResult(void)
{
	double instData;	// 最新データ
	double avgResult;	// 平均結果
	unsigned int calcData;
	
	// 最新データが99999.5~100000.5の範囲をランダムに変動(rand関数が正規分布でないのは承知)
	calcData = 99999500 + (rand() % 1000);
	instData = (double)calcData / 1000.0f;
	printf("avgData[%3d] = %10.3f", avgPtr, instData);
	avgData[avgPtr] = instData;	// 最新データをバッファに格納
	avgSumData += instData;		// 平均用SUM値に最新データ加算
	if (++avgPtr >= 100) avgPtr = 0;
	avgResult = avgSumData / 100;
	printf("  avgSumData = %12.3f  avgResult = %10.3f\n",
		avgSumData, avgResult);
	avgSumData -= avgData[avgPtr];// もっとも古いデータを減算(FIFO)
}

int main(void)
{
	srand(time(0));
	for (int i = 0; i < 200; i++)
		GetAvgResult();
}
実行結果

コード:

avgData[  0] = 100000.103  avgSumData =   100000.103  avgResult =   1000.001
avgData[  1] =  99999.985  avgSumData =   200000.088  avgResult =   2000.001
avgData[  2] = 100000.269  avgSumData =   300000.357  avgResult =   3000.004
avgData[  3] =  99999.933  avgSumData =   400000.290  avgResult =   4000.003
avgData[  4] =  99999.637  avgSumData =   499999.927  avgResult =   4999.999
avgData[  5] =  99999.957  avgSumData =   599999.884  avgResult =   5999.999
avgData[  6] = 100000.311  avgSumData =   700000.195  avgResult =   7000.002
avgData[  7] =  99999.562  avgSumData =   799999.757  avgResult =   7999.998
avgData[  8] = 100000.320  avgSumData =   900000.077  avgResult =   9000.001
avgData[  9] = 100000.476  avgSumData =  1000000.553  avgResult =  10000.006
avgData[ 10] = 100000.135  avgSumData =  1100000.688  avgResult =  11000.007
avgData[ 11] = 100000.365  avgSumData =  1200001.053  avgResult =  12000.011
avgData[ 12] = 100000.235  avgSumData =  1300001.288  avgResult =  13000.013
avgData[ 13] = 100000.488  avgSumData =  1400001.776  avgResult =  14000.018
avgData[ 14] =  99999.583  avgSumData =  1500001.359  avgResult =  15000.014
avgData[ 15] = 100000.387  avgSumData =  1600001.746  avgResult =  16000.017
avgData[ 16] =  99999.668  avgSumData =  1700001.414  avgResult =  17000.014
avgData[ 17] =  99999.930  avgSumData =  1800001.344  avgResult =  18000.013
avgData[ 18] =  99999.752  avgSumData =  1900001.096  avgResult =  19000.011
avgData[ 19] =  99999.780  avgSumData =  2000000.876  avgResult =  20000.009
avgData[ 20] = 100000.334  avgSumData =  2100001.210  avgResult =  21000.012
avgData[ 21] =  99999.708  avgSumData =  2200000.918  avgResult =  22000.009
avgData[ 22] = 100000.155  avgSumData =  2300001.073  avgResult =  23000.011
avgData[ 23] = 100000.227  avgSumData =  2400001.300  avgResult =  24000.013
avgData[ 24] =  99999.562  avgSumData =  2500000.862  avgResult =  25000.009
avgData[ 25] = 100000.237  avgSumData =  2600001.099  avgResult =  26000.011
avgData[ 26] = 100000.120  avgSumData =  2700001.219  avgResult =  27000.012
avgData[ 27] = 100000.113  avgSumData =  2800001.332  avgResult =  28000.013
avgData[ 28] = 100000.271  avgSumData =  2900001.603  avgResult =  29000.016
avgData[ 29] =  99999.832  avgSumData =  3000001.435  avgResult =  30000.014
avgData[ 30] =  99999.712  avgSumData =  3100001.147  avgResult =  31000.011
avgData[ 31] =  99999.894  avgSumData =  3200001.041  avgResult =  32000.010
avgData[ 32] = 100000.271  avgSumData =  3300001.312  avgResult =  33000.013
avgData[ 33] =  99999.903  avgSumData =  3400001.215  avgResult =  34000.012
avgData[ 34] = 100000.478  avgSumData =  3500001.693  avgResult =  35000.017
avgData[ 35] = 100000.442  avgSumData =  3600002.135  avgResult =  36000.021
avgData[ 36] = 100000.180  avgSumData =  3700002.315  avgResult =  37000.023
avgData[ 37] = 100000.388  avgSumData =  3800002.703  avgResult =  38000.027
avgData[ 38] = 100000.323  avgSumData =  3900003.026  avgResult =  39000.030
avgData[ 39] = 100000.361  avgSumData =  4000003.387  avgResult =  40000.034
avgData[ 40] =  99999.547  avgSumData =  4100002.934  avgResult =  41000.029
avgData[ 41] =  99999.697  avgSumData =  4200002.631  avgResult =  42000.026
avgData[ 42] = 100000.074  avgSumData =  4300002.705  avgResult =  43000.027
avgData[ 43] = 100000.454  avgSumData =  4400003.159  avgResult =  44000.032
avgData[ 44] = 100000.103  avgSumData =  4500003.262  avgResult =  45000.033
avgData[ 45] = 100000.441  avgSumData =  4600003.703  avgResult =  46000.037
avgData[ 46] = 100000.392  avgSumData =  4700004.095  avgResult =  47000.041
avgData[ 47] =  99999.633  avgSumData =  4800003.728  avgResult =  48000.037
avgData[ 48] =  99999.970  avgSumData =  4900003.698  avgResult =  49000.037
avgData[ 49] =  99999.686  avgSumData =  5000003.384  avgResult =  50000.034
avgData[ 50] =  99999.960  avgSumData =  5100003.344  avgResult =  51000.033
avgData[ 51] =  99999.827  avgSumData =  5200003.171  avgResult =  52000.032
avgData[ 52] =  99999.568  avgSumData =  5300002.739  avgResult =  53000.027
avgData[ 53] =  99999.531  avgSumData =  5400002.270  avgResult =  54000.023
avgData[ 54] = 100000.063  avgSumData =  5500002.333  avgResult =  55000.023
avgData[ 55] = 100000.121  avgSumData =  5600002.454  avgResult =  56000.025
avgData[ 56] = 100000.004  avgSumData =  5700002.458  avgResult =  57000.025
avgData[ 57] = 100000.461  avgSumData =  5800002.919  avgResult =  58000.029
avgData[ 58] = 100000.107  avgSumData =  5900003.026  avgResult =  59000.030
avgData[ 59] =  99999.888  avgSumData =  6000002.914  avgResult =  60000.029
avgData[ 60] = 100000.350  avgSumData =  6100003.264  avgResult =  61000.033
avgData[ 61] = 100000.406  avgSumData =  6200003.670  avgResult =  62000.037
avgData[ 62] = 100000.121  avgSumData =  6300003.791  avgResult =  63000.038
avgData[ 63] =  99999.873  avgSumData =  6400003.664  avgResult =  64000.037
avgData[ 64] =  99999.997  avgSumData =  6500003.661  avgResult =  65000.037
avgData[ 65] =  99999.906  avgSumData =  6600003.567  avgResult =  66000.036
avgData[ 66] = 100000.008  avgSumData =  6700003.575  avgResult =  67000.036
avgData[ 67] = 100000.189  avgSumData =  6800003.764  avgResult =  68000.038
avgData[ 68] = 100000.018  avgSumData =  6900003.782  avgResult =  69000.038
avgData[ 69] = 100000.142  avgSumData =  7000003.924  avgResult =  70000.039
avgData[ 70] =  99999.832  avgSumData =  7100003.756  avgResult =  71000.038
avgData[ 71] = 100000.434  avgSumData =  7200004.190  avgResult =  72000.042
avgData[ 72] =  99999.733  avgSumData =  7300003.923  avgResult =  73000.039
avgData[ 73] =  99999.917  avgSumData =  7400003.840  avgResult =  74000.038
avgData[ 74] =  99999.983  avgSumData =  7500003.823  avgResult =  75000.038
avgData[ 75] =  99999.886  avgSumData =  7600003.709  avgResult =  76000.037
avgData[ 76] = 100000.189  avgSumData =  7700003.898  avgResult =  77000.039
avgData[ 77] = 100000.301  avgSumData =  7800004.199  avgResult =  78000.042
avgData[ 78] = 100000.261  avgSumData =  7900004.460  avgResult =  79000.045
avgData[ 79] =  99999.971  avgSumData =  8000004.431  avgResult =  80000.044
avgData[ 80] =  99999.554  avgSumData =  8100003.985  avgResult =  81000.040
avgData[ 81] =  99999.986  avgSumData =  8200003.971  avgResult =  82000.040
avgData[ 82] =  99999.822  avgSumData =  8300003.793  avgResult =  83000.038
avgData[ 83] =  99999.908  avgSumData =  8400003.701  avgResult =  84000.037
avgData[ 84] = 100000.116  avgSumData =  8500003.817  avgResult =  85000.038
avgData[ 85] = 100000.481  avgSumData =  8600004.298  avgResult =  86000.043
avgData[ 86] = 100000.232  avgSumData =  8700004.530  avgResult =  87000.045
avgData[ 87] =  99999.631  avgSumData =  8800004.161  avgResult =  88000.042
avgData[ 88] = 100000.169  avgSumData =  8900004.330  avgResult =  89000.043
avgData[ 89] = 100000.488  avgSumData =  9000004.818  avgResult =  90000.048
avgData[ 90] =  99999.901  avgSumData =  9100004.719  avgResult =  91000.047
avgData[ 91] = 100000.363  avgSumData =  9200005.082  avgResult =  92000.051
avgData[ 92] =  99999.987  avgSumData =  9300005.069  avgResult =  93000.051
avgData[ 93] = 100000.428  avgSumData =  9400005.497  avgResult =  94000.055
avgData[ 94] =  99999.755  avgSumData =  9500005.252  avgResult =  95000.053
avgData[ 95] = 100000.316  avgSumData =  9600005.568  avgResult =  96000.056
avgData[ 96] =  99999.673  avgSumData =  9700005.241  avgResult =  97000.052
avgData[ 97] =  99999.850  avgSumData =  9800005.091  avgResult =  98000.051
avgData[ 98] = 100000.091  avgSumData =  9900005.182  avgResult =  99000.052
avgData[ 99] = 100000.365  avgSumData = 10000005.547  avgResult = 100000.055
avgData[  0] = 100000.232  avgSumData = 10000005.676  avgResult = 100000.057
avgData[  1] = 100000.474  avgSumData = 10000006.165  avgResult = 100000.062
avgData[  2] = 100000.031  avgSumData = 10000005.927  avgResult = 100000.059
avgData[  3] = 100000.470  avgSumData = 10000006.464  avgResult = 100000.065
avgData[  4] =  99999.776  avgSumData = 10000006.603  avgResult = 100000.066
avgData[  5] =  99999.801  avgSumData = 10000006.447  avgResult = 100000.064
avgData[  6] = 100000.063  avgSumData = 10000006.199  avgResult = 100000.062
avgData[  7] = 100000.398  avgSumData = 10000007.035  avgResult = 100000.070
avgData[  8] = 100000.171  avgSumData = 10000006.886  avgResult = 100000.069
avgData[  9] = 100000.454  avgSumData = 10000006.864  avgResult = 100000.069
avgData[ 10] =  99999.837  avgSumData = 10000006.566  avgResult = 100000.066
avgData[ 11] =  99999.578  avgSumData = 10000005.779  avgResult = 100000.058
avgData[ 12] = 100000.316  avgSumData = 10000005.860  avgResult = 100000.059
avgData[ 13] = 100000.103  avgSumData = 10000005.475  avgResult = 100000.055
avgData[ 14] = 100000.088  avgSumData = 10000005.980  avgResult = 100000.060
avgData[ 15] = 100000.262  avgSumData = 10000005.855  avgResult = 100000.059
avgData[ 16] =  99999.757  avgSumData = 10000005.944  avgResult = 100000.059
avgData[ 17] =  99999.957  avgSumData = 10000005.971  avgResult = 100000.060
avgData[ 18] =  99999.798  avgSumData = 10000006.017  avgResult = 100000.060
avgData[ 19] =  99999.843  avgSumData = 10000006.080  avgResult = 100000.061
avgData[ 20] =  99999.612  avgSumData = 10000005.358  avgResult = 100000.054
avgData[ 21] = 100000.014  avgSumData = 10000005.664  avgResult = 100000.057
avgData[ 22] = 100000.302  avgSumData = 10000005.811  avgResult = 100000.058
avgData[ 23] = 100000.170  avgSumData = 10000005.754  avgResult = 100000.058
avgData[ 24] = 100000.246  avgSumData = 10000006.438  avgResult = 100000.064
avgData[ 25] =  99999.969  avgSumData = 10000006.170  avgResult = 100000.062
avgData[ 26] =  99999.821  avgSumData = 10000005.871  avgResult = 100000.059
avgData[ 27] = 100000.184  avgSumData = 10000005.942  avgResult = 100000.059
avgData[ 28] = 100000.467  avgSumData = 10000006.138  avgResult = 100000.061
avgData[ 29] =  99999.583  avgSumData = 10000005.889  avgResult = 100000.059
avgData[ 30] = 100000.230  avgSumData = 10000006.407  avgResult = 100000.064
avgData[ 31] =  99999.723  avgSumData = 10000006.236  avgResult = 100000.062
avgData[ 32] = 100000.061  avgSumData = 10000006.026  avgResult = 100000.060
avgData[ 33] =  99999.856  avgSumData = 10000005.979  avgResult = 100000.060
avgData[ 34] =  99999.695  avgSumData = 10000005.196  avgResult = 100000.052
avgData[ 35] = 100000.028  avgSumData = 10000004.782  avgResult = 100000.048
avgData[ 36] = 100000.420  avgSumData = 10000005.022  avgResult = 100000.050
avgData[ 37] = 100000.379  avgSumData = 10000005.013  avgResult = 100000.050
avgData[ 38] = 100000.178  avgSumData = 10000004.868  avgResult = 100000.049
avgData[ 39] =  99999.749  avgSumData = 10000004.256  avgResult = 100000.043
avgData[ 40] = 100000.195  avgSumData = 10000004.904  avgResult = 100000.049
avgData[ 41] =  99999.659  avgSumData = 10000004.866  avgResult = 100000.049
avgData[ 42] = 100000.194  avgSumData = 10000004.986  avgResult = 100000.050
avgData[ 43] = 100000.269  avgSumData = 10000004.801  avgResult = 100000.048
avgData[ 44] = 100000.465  avgSumData = 10000005.163  avgResult = 100000.052
avgData[ 45] = 100000.302  avgSumData = 10000005.024  avgResult = 100000.050
avgData[ 46] =  99999.711  avgSumData = 10000004.343  avgResult = 100000.043
avgData[ 47] =  99999.618  avgSumData = 10000004.328  avgResult = 100000.043
avgData[ 48] =  99999.960  avgSumData = 10000004.318  avgResult = 100000.043
avgData[ 49] =  99999.503  avgSumData = 10000004.135  avgResult = 100000.041
avgData[ 50] =  99999.947  avgSumData = 10000004.122  avgResult = 100000.041
avgData[ 51] =  99999.558  avgSumData = 10000003.853  avgResult = 100000.039
avgData[ 52] =  99999.730  avgSumData = 10000004.015  avgResult = 100000.040
avgData[ 53] = 100000.103  avgSumData = 10000004.587  avgResult = 100000.046
avgData[ 54] =  99999.541  avgSumData = 10000004.065  avgResult = 100000.041
avgData[ 55] =  99999.501  avgSumData = 10000003.445  avgResult = 100000.034
avgData[ 56] =  99999.813  avgSumData = 10000003.254  avgResult = 100000.033
avgData[ 57] =  99999.551  avgSumData = 10000002.344  avgResult = 100000.023
avgData[ 58] = 100000.182  avgSumData = 10000002.419  avgResult = 100000.024
avgData[ 59] = 100000.149  avgSumData = 10000002.680  avgResult = 100000.027
avgData[ 60] =  99999.860  avgSumData = 10000002.190  avgResult = 100000.022
avgData[ 61] =  99999.625  avgSumData = 10000001.409  avgResult = 100000.014
avgData[ 62] = 100000.363  avgSumData = 10000001.651  avgResult = 100000.017
avgData[ 63] =  99999.503  avgSumData = 10000001.281  avgResult = 100000.013
avgData[ 64] =  99999.673  avgSumData = 10000000.957  avgResult = 100000.010
avgData[ 65] = 100000.208  avgSumData = 10000001.259  avgResult = 100000.013
avgData[ 66] = 100000.490  avgSumData = 10000001.741  avgResult = 100000.017
avgData[ 67] = 100000.077  avgSumData = 10000001.629  avgResult = 100000.016
avgData[ 68] = 100000.183  avgSumData = 10000001.794  avgResult = 100000.018
avgData[ 69] =  99999.749  avgSumData = 10000001.401  avgResult = 100000.014
avgData[ 70] =  99999.696  avgSumData = 10000001.265  avgResult = 100000.013
avgData[ 71] = 100000.029  avgSumData = 10000000.860  avgResult = 100000.009
avgData[ 72] =  99999.565  avgSumData = 10000000.692  avgResult = 100000.007
avgData[ 73] = 100000.195  avgSumData = 10000000.970  avgResult = 100000.010
avgData[ 74] =  99999.682  avgSumData = 10000000.669  avgResult = 100000.007
avgData[ 75] = 100000.037  avgSumData = 10000000.820  avgResult = 100000.008
avgData[ 76] =  99999.520  avgSumData = 10000000.151  avgResult = 100000.002
avgData[ 77] = 100000.285  avgSumData = 10000000.135  avgResult = 100000.001
avgData[ 78] =  99999.683  avgSumData =  9999999.557  avgResult =  99999.996
avgData[ 79] = 100000.031  avgSumData =  9999999.617  avgResult =  99999.996
avgData[ 80] =  99999.591  avgSumData =  9999999.654  avgResult =  99999.997
avgData[ 81] =  99999.647  avgSumData =  9999999.315  avgResult =  99999.993
avgData[ 82] = 100000.285  avgSumData =  9999999.778  avgResult =  99999.998
avgData[ 83] = 100000.447  avgSumData = 10000000.317  avgResult = 100000.003
avgData[ 84] =  99999.933  avgSumData = 10000000.134  avgResult = 100000.001
avgData[ 85] = 100000.419  avgSumData = 10000000.072  avgResult = 100000.001
avgData[ 86] =  99999.891  avgSumData =  9999999.731  avgResult =  99999.997
avgData[ 87] = 100000.498  avgSumData = 10000000.598  avgResult = 100000.006
avgData[ 88] = 100000.378  avgSumData = 10000000.807  avgResult = 100000.008
avgData[ 89] =  99999.677  avgSumData =  9999999.996  avgResult = 100000.000
avgData[ 90] = 100000.287  avgSumData = 10000000.382  avgResult = 100000.004
avgData[ 91] =  99999.754  avgSumData =  9999999.773  avgResult =  99999.998
avgData[ 92] =  99999.643  avgSumData =  9999999.429  avgResult =  99999.994
avgData[ 93] =  99999.560  avgSumData =  9999998.561  avgResult =  99999.986
avgData[ 94] = 100000.000  avgSumData =  9999998.806  avgResult =  99999.988
avgData[ 95] =  99999.736  avgSumData =  9999998.226  avgResult =  99999.982
avgData[ 96] =  99999.932  avgSumData =  9999998.485  avgResult =  99999.985
avgData[ 97] = 100000.434  avgSumData =  9999999.069  avgResult =  99999.991
avgData[ 98] = 100000.198  avgSumData =  9999999.176  avgResult =  99999.992
avgData[ 99] =  99999.993  avgSumData =  9999998.804  avgResult =  99999.988

celica

Re: 浮動小数点の加減算による誤差蓄積について

#8

投稿記事 by celica » 5年前

かずまさん、早速のご回答ありがとうございます。
以下、ご指摘頂いた点に対して回答いたします。

>もっとも古いデータを減算したら、データは 99個しかありません。
>それを 100 で割ったら、結果が小さくなるのは当然です。
100個の配列データの総和に対して、最新データを加算してから古いデータを減算しているので、
分母は100で問題ないと考えています。

>最初の質問の現象と違いますね。
混乱させて申し訳ございません。
前投稿で記載させて頂きましたが、
実際のプログラムは簡略化したプログラムよりも複雑になっており、そのプログラムでは増加現象が発生しています(増加現象が再現できるプログラムを作成したかったのですが、減少するプログラムしか作成出来ませんでした)。
増加・減少の違いも気にはなっているのですが、平均結果が元データのバラつき範囲外になることが疑問点です。
floatの精度についても、doubuleにすれば改善することも承知しておりますが、この疑問点を解消したくて投稿させて頂きました。

かずま

Re: 浮動小数点の加減算による誤差蓄積について

#9

投稿記事 by かずま » 5年前

celica さんが書きました:
5年前
100個の配列データの総和に対して、最新データを加算してから古いデータを減算しているので、
分母は100で問題ないと考えています。
データの個数を 100 から 5 に変えてみましょう。
データには、1 から 10 までの 10個を与えてみましょう。
引いてから、割るとどうなるか。

コード:

#include <stdio.h>

unsigned int avgPtr;
double avgData[5];  // 各要素に初期値:100000を格納済み
double avgSumData;    // 初期値:10000000を格納済み

double k = 0;

static void GetAvgResult(void)
{
	double instData;	// 最新データ
	double avgResult;	// 平均結果
	
	instData = ++k;
	printf("avgData[%3d] = %10.3f", avgPtr, instData);
	avgData[avgPtr] = instData;	// 最新データをバッファに格納
	avgSumData += instData;		// 平均用SUM値に最新データ加算
	if (++avgPtr >= 5) avgPtr = 0;
	avgSumData -= avgData[avgPtr];// もっとも古いデータを減算(FIFO)
	avgResult = avgSumData / 5;
	printf("  avgSumData = %12.3f  avgResult = %10.3f\n",
		avgSumData, avgResult);
}

int main(void)
{
	srand(time(0));
	for (int i = 0; i < 10; i++)
		GetAvgResult();
}
実行結果

コード:

avgData[  0] =      1.000  avgSumData =        1.000  avgResult =      0.200
avgData[  1] =      2.000  avgSumData =        3.000  avgResult =      0.600
avgData[  2] =      3.000  avgSumData =        6.000  avgResult =      1.200
avgData[  3] =      4.000  avgSumData =       10.000  avgResult =      2.000
avgData[  4] =      5.000  avgSumData =       14.000  avgResult =      2.800
avgData[  0] =      6.000  avgSumData =       18.000  avgResult =      3.600
avgData[  1] =      7.000  avgSumData =       22.000  avgResult =      4.400
avgData[  2] =      8.000  avgSumData =       26.000  avgResult =      5.200
avgData[  3] =      9.000  avgSumData =       30.000  avgResult =      6.000
avgData[  4] =     10.000  avgSumData =       34.000  avgResult =      6.800
最新の 5個(6~10) の平均値が 6.8 と出ました。
(6+7+8+9+10)/5 = 8.0 のはずなのに、
(7+8+9+10)/5 = 6.8 と計算しています。

次のように変更して、割ってから引くと、

コード:

	avgResult = avgSumData / 5;
	printf("  avgSumData = %12.3f  avgResult = %10.3f\n",
		avgSumData, avgResult);
	avgSumData -= avgData[avgPtr];// もっとも古いデータを減算(FIFO)
実行結果

コード:

avgData[  0] =      1.000  avgSumData =        1.000  avgResult =      0.200
avgData[  1] =      2.000  avgSumData =        3.000  avgResult =      0.600
avgData[  2] =      3.000  avgSumData =        6.000  avgResult =      1.200
avgData[  3] =      4.000  avgSumData =       10.000  avgResult =      2.000
avgData[  4] =      5.000  avgSumData =       15.000  avgResult =      3.000
avgData[  0] =      6.000  avgSumData =       20.000  avgResult =      4.000
avgData[  1] =      7.000  avgSumData =       25.000  avgResult =      5.000
avgData[  2] =      8.000  avgSumData =       30.000  avgResult =      6.000
avgData[  3] =      9.000  avgSumData =       35.000  avgResult =      7.000
avgData[  4] =     10.000  avgSumData =       40.000  avgResult =      8.000
ちゃんと 8 になりました。

celica

Re: 浮動小数点の加減算による誤差蓄積について

#10

投稿記事 by celica » 5年前

リングバッファのサイズが一つ足りていなかったようです。
データ個数が101個必要でした。失礼致しました。
データ数量を見直しても同現象は改善されませんでした。

かずま

Re: 浮動小数点の加減算による誤差蓄積について

#11

投稿記事 by かずま » 5年前

celica さんが書きました:
5年前
リングバッファのサイズが一つ足りていなかったようです。
データ個数が101個必要でした。失礼致しました。
それでもいいけど、なぜ割ってから引くという修正方法にしないのですか?
celica さんが書きました:
5年前
データ数量を見直しても同現象は改善されませんでした。
現象が再現する実際のコードを見ないと、これ以上アドバイスできません。

かずま

Re: 浮動小数点の加減算による誤差蓄積について

#12

投稿記事 by かずま » 5年前

今回のやり取りから類推できることは、
増加現象を再現できる実際のプログラムで、
100個のデータを 99 で割っている、あるいは
101個のデータを 100で割っている可能性がある、ということです。

増加現象を再現できる実際のプログラムが公表できないのなら、
私がやったように、デバッグ実行したときの
入力値、合計値、平均値のデータを見せてもらえませんか?

返信

“C言語何でも質問掲示板” へ戻る