ページ 1 / 1
浮動小数点の加減算による誤差蓄積について
Posted: 2018年10月04日(木) 18:55
by celica
件名の内容で質問させて頂きます。
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;
}
Re: 浮動小数点の加減算による誤差蓄積について
Posted: 2018年10月04日(木) 20:36
by 結城紬
celicaさん、こんにちは。
「徐々に増加する」とは、何件あたりどのくらいで、どこまで増加するのですか?
例えば、データが均一ではなく、大きい方に偏っていれば、徐々に増加する場合もありますが、それとは違いますか?
Re: 浮動小数点の加減算による誤差蓄積について
Posted: 2018年10月04日(木) 21:25
by celica
ご返信頂き、有難うございます。
実際のプログラムはもう少し複雑で、記載させて頂いたプログラムは簡略化したものになります。
そのため増加量について定量的に表せないのですが、
イメージとしては1演算当たりのズレ量は微小で、長時間演算(繰り返し演算)するとズレが蓄積されていき、
最終的には演算結果が元々のランダムデータの最大値100.1を超えて増加していくようなイメージです。
例えば、
加算時(avgSumData += instData)の情報落ちが必ず切り捨て(avgSumDataが真値より大きめになる)
減算時(avgSumData -= avgData[avgPtr])の情報落ちが四捨五入(avgSumDataが真値より大きくなったり、小さくなったりする)
という処理が行われていると徐々に増加すると考えましたが、
float型の有効数字桁以外の処理がどうなっているのかが分からず、
このような現象が起こり得るのかどうか、というのが疑問点になります。
Re: 浮動小数点の加減算による誤差蓄積について
Posted: 2018年10月04日(木) 22:50
by かずま
その簡略化したプログラムは間違っています。
入力データが 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 にしても
質問にある現象は再現しません。
現象が再現する簡略化プログラムを提示してください。
Re: 浮動小数点の加減算による誤差蓄積について
Posted: 2018年10月12日(金) 11:59
by celica
ご返信有難うございます。
遅くなりましたが、再現する簡易プログラムを以下に記述します。
コード:
// 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: 浮動小数点の加減算による誤差蓄積について
Posted: 2018年10月12日(金) 14:34
by かずま
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: 浮動小数点の加減算による誤差蓄積について
Posted: 2018年10月12日(金) 16:04
by かずま
ひとつ言っておきます。
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
Re: 浮動小数点の加減算による誤差蓄積について
Posted: 2018年10月12日(金) 17:19
by celica
かずまさん、早速のご回答ありがとうございます。
以下、ご指摘頂いた点に対して回答いたします。
>もっとも古いデータを減算したら、データは 99個しかありません。
>それを 100 で割ったら、結果が小さくなるのは当然です。
100個の配列データの総和に対して、最新データを加算してから古いデータを減算しているので、
分母は100で問題ないと考えています。
>最初の質問の現象と違いますね。
混乱させて申し訳ございません。
前投稿で記載させて頂きましたが、
実際のプログラムは簡略化したプログラムよりも複雑になっており、そのプログラムでは増加現象が発生しています(増加現象が再現できるプログラムを作成したかったのですが、減少するプログラムしか作成出来ませんでした)。
増加・減少の違いも気にはなっているのですが、平均結果が元データのバラつき範囲外になることが疑問点です。
floatの精度についても、doubuleにすれば改善することも承知しておりますが、この疑問点を解消したくて投稿させて頂きました。
Re: 浮動小数点の加減算による誤差蓄積について
Posted: 2018年10月12日(金) 18:26
by かずま
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 になりました。
Re: 浮動小数点の加減算による誤差蓄積について
Posted: 2018年10月12日(金) 20:52
by celica
リングバッファのサイズが一つ足りていなかったようです。
データ個数が101個必要でした。失礼致しました。
データ数量を見直しても同現象は改善されませんでした。
Re: 浮動小数点の加減算による誤差蓄積について
Posted: 2018年10月13日(土) 07:26
by かずま
celica さんが書きました: ↑5年前
リングバッファのサイズが一つ足りていなかったようです。
データ個数が101個必要でした。失礼致しました。
それでもいいけど、なぜ割ってから引くという修正方法にしないのですか?
celica さんが書きました: ↑5年前
データ数量を見直しても同現象は改善されませんでした。
現象が再現する実際のコードを見ないと、これ以上アドバイスできません。
Re: 浮動小数点の加減算による誤差蓄積について
Posted: 2018年10月14日(日) 09:39
by かずま
今回のやり取りから類推できることは、
増加現象を再現できる実際のプログラムで、
100個のデータを 99 で割っている、あるいは
101個のデータを 100で割っている可能性がある、ということです。
増加現象を再現できる実際のプログラムが公表できないのなら、
私がやったように、デバッグ実行したときの
入力値、合計値、平均値のデータを見せてもらえませんか?