Skip to content

Instantly share code, notes, and snippets.

@komiya-atsushi
Created April 1, 2013 07:34
Show Gist options
  • Save komiya-atsushi/5283676 to your computer and use it in GitHub Desktop.
Save komiya-atsushi/5283676 to your computer and use it in GitHub Desktop.
情報落ちを回避する加算処理はこんなものかな?
/**
* なるべく精度を保ちつつ加算する機能を提供します。
* <p>
* 情報落ちを回避する仕組みを備えています。
* </p>
*
* @author KOMIYA Atsushi
*/
public class AccurateAdder {
/** sum より大きな値を一時的に蓄積するバッファのサイズです */
private final int bufferSize;
/** 加算された値を保持します */
private double sum;
/** sum に加算するには小さ過ぎる値(誤差)を保持します */
private double error;
/** sum より大きな値を格納するバッファです */
private double[] buffer;
/** バッファに格納されている値の個数を表します */
private int bufferedCount;
/** 計算途中で利用するワークメモリ的な変数です */
private double temp;
public AccurateAdder() {
this(10);
}
public AccurateAdder(int bufSize) {
this.bufferSize = bufSize;
this.buffer = new double[bufSize];
}
/**
* 加算結果を返却します。
*
* @return
*/
public double sum() {
flushBuffer();
return sum;
}
/**
* 加算結果に加算することのできない累積誤差を返却します。
*
* @return
*/
public double error() {
flushBuffer();
return error;
}
/**
* 情報落ちを極力回避しながら加算します。
* <p>
* value が現状の sum よりも大きな値の場合は、sum に加算するのを遅延させます。 value が sum よりも小さければ、現状の
* error と value を先に足し合わせ、その結果を sum に加えます。
* </p>
*
* @param value
*/
public void add(double value) {
if (value < sum) {
temp = (value + error) + sum;
error = (value + error) - (temp - sum);
sum = temp;
} else {
buffer[bufferedCount++] = value;
if (bufferedCount == bufferSize) {
flushBuffer();
}
}
}
/**
* バッファに蓄積されている値を sum に足し込みます。
*/
private void flushBuffer() {
if (bufferedCount == 0) {
return;
}
Arrays.sort(buffer);
for (int i = 0; i < bufferSize; i++) {
double v = buffer[i];
if (v < sum) {
temp = (v + error) + sum;
error = (v + error) - (temp - sum);
} else {
temp = v + (error + sum);
error = (error + sum) - (temp - v);
}
sum = temp;
}
bufferedCount = 0;
Arrays.fill(buffer, 0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment