概要

データ集計(SUM)を行った時に、合計値が期待値と僅かにズレたことありませんか?
もちろん、そもそも集計方法が間違ってる可能性もあります。
しかし、例え集計方法が正しくても、上記の現象が起こる時があります。
その1ケースが「情報落ち」という現象です。

情報落ち」とは、「(絶対値的に)差が大きい数値の足し算や引き算にて、小さい方の値が計算結果に反映されない」という現象です。
何故「情報落ち」が発生するのか?
それは、「コンピュータでは数値に割り当てるバイトが決まっているから」です。

コンピュータでは、割り当てられたバイト内にて、”浮動小数点表現”等の、特定の方式で数値情報を格納しています。
その格納方式・バイト数によって、有効桁数が決まり、その有効桁数で表現しきれない時に情報落ちが発生します。
※有効桁数:「位(くらい)を示すだけの0を除いた、意味ある数字」を保持できる桁数

情報落ちは防げないのか?
いえ、対策はあります。

対策ですが、足し算の場合は、「小さい値同士を先に足し合わせ、その値と大きい値を足し合わせる」です。
要するに、2項演算(+-)における2項の差を小さくすれば情報落ちが防げるので、その観点で引き算も対策すれば良いです。

詳細

浮動小数点表現

上記の通り、浮動小数点表現は、コンピュータでの数値の格納方式の一つです。
数値を指数表記にした時の各情報を格納する形になっています。
下図の通り、与えられたビットに対して3つの部が定められ、それぞれの部に対して、対応する情報を格納します。
下記例の通り、仮数部で表現できる桁数が有効桁数になります。

例えば、「123.45」という数値を格納するとします。
この時、下式の通りに指数表記できます。

123.45 = 12345 ×10^{-2}

このように指数表記して、右辺の「+-」を符号部、「12345」を仮数部、(10の指数である)「-2」を指数部に格納する形になります。

[SAS] 情報落ち、対策の結果を実際に確認してみよう。

概要

(絶対値的に)差の大きい数値について、下記2点を実際に確認してみましょう。
SASで集計用データセットを作り、下記1 , 2それぞれの足し合わせを行ってみます。

  1. [情報落ち] 大きい数と小さい数を交互に足したら、小さい数が結果に反映されない
  2. [対策] 先に小さい数を足し、その後大きい数を足せば、小さい数も結果に反映される。

[SAS] コード

集計用データセット(work.data)では、大きい数値として、1京、1,000兆を持つオブザベーションと、小さい数値として、1を持つオブザベーションを作成します。(変数「x」に数値を格納)

集計結果はデータセット「work.result」に出力します。
変数「sum_1」に[情報落ち]の結果、「sum_2」に[対策]の結果が入ります。

/* どでかい数値と小さい数値のデータを作成 */
/* -> どでかい:1京、1000兆 */
/*   小さい:1 */
data data;
	do id_1 = 1 to 4;
		/* id_1=1 */
		if id_1=1 then do;
			/* どでかい数値① : 1京 (10,000,000,000,000,000)*/
			x = 10000000000000000;
			output;
		end;
		/* id_1が奇数(id_1=1を除く) */
		else if mod(id_1 , 2)=1 then do;
			/* どでかい数値② : 1,000兆 (1,000,000,000,000,000)*/
			x = 1000000000000000;
			output;
		end;
		/* id_1が偶数*/
		else do;
			/* 小さな数値 : 1 */
			x = 1;
			output;
		end;
	end;
run;

/* 1 と 1京 が交互になるようにソート */
proc sort data=data;
by id_1;
run;

/* sum_1 : 1と1京を交互に足し合わせる */
data data;
attrib id_1 x format=comma18. sum_1 format=comma18.;
set data;
retain sum_1 0;
sum_1 = sum_1 + x;
run;

/* 1のデータの次に1京のデータが来るようにソート */
proc sort data=data;
by x;
run;

/* sum_2 : 1を足し合わせた後に、1京を足し合わせる */
data data;
attrib id_1 id_2 x format=comma18. sum_1 format=comma18. sum_2 format=comma18.;
set data;
retain id_2 0 sum_2 0;
id_2 = id_2 + 1;
sum_2 = sum_2 + x;
run;

/* 集計結果=max */
proc means data=data noprint;
var sum_1 sum_2;
output out=result / autoname;
run;

data result;
set result;
keep sum_1 sum_2;
attrib sum_1 format=comma18. sum_2 format=comma18.;
where _STAT_ = 'MAX';
run;

[SAS] 結果

集計結果 (work.result)

理論値は「1京1000兆2」(1京 + 1,000兆 + 2)ですが、[情報落ち]の計算結果(sum_1)では 2 が反映されていません。
それに対し、[対策]の計算結果(sum_2)は理論値と一致しました。

sum_1sum_2
1100000000000000011000000000000002
work.result
集計元+集計過程 (work.data)

変数「id_1」と「sum_1」は、[情報落ち]分の“足し合わせ順”“足し合わせ過程”、「id_2」と「sum_2」は[対策]分です。
[情報落ち]の足し合わせ過程(sum_1)を確認すると、小さい数の 1 が反映されていないことが分かります。

id_1id_2xsum_1sum_2
14100000000000000001000000000000000011000000000000002
211100000000000000001
331000000000000000110000000000000001000000000000002
421110000000000000002
work.data

By clear

データエンジニア・機械学習・分析等を主とし、Webアプリ開発も行っているフリーランスです。