概要
データ集計(SUM)を行った時に、合計値が期待値と僅かにズレたことありませんか?
もちろん、そもそも集計方法が間違ってる可能性もあります。
しかし、例え集計方法が正しくても、上記の現象が起こる時があります。
その1ケースが「情報落ち」という現象です。
「情報落ち」とは、「(絶対値的に)差が大きい数値の足し算や引き算にて、小さい方の値が計算結果に反映されない」という現象です。
何故「情報落ち」が発生するのか?
それは、「コンピュータでは数値に割り当てるバイトが決まっているから」です。
コンピュータでは、割り当てられたバイト内にて、”浮動小数点表現”等の、特定の方式で数値情報を格納しています。
その格納方式・バイト数によって、有効桁数が決まり、その有効桁数で表現しきれない時に情報落ちが発生します。
※有効桁数:「位(くらい)を示すだけの0を除いた、意味ある数字」を保持できる桁数
情報落ちは防げないのか?
いえ、対策はあります。
対策ですが、足し算の場合は、「小さい値同士を先に足し合わせ、その値と大きい値を足し合わせる」です。
要するに、2項演算(+-)における2項の差を小さくすれば情報落ちが防げるので、その観点で引き算も対策すれば良いです。
詳細
浮動小数点表現
上記の通り、浮動小数点表現は、コンピュータでの数値の格納方式の一つです。
数値を指数表記にした時の各情報を格納する形になっています。
下図の通り、与えられたビットに対して3つの部が定められ、それぞれの部に対して、対応する情報を格納します。
下記例の通り、仮数部で表現できる桁数が有効桁数になります。
例えば、「123.45」という数値を格納するとします。
この時、下式の通りに指数表記できます。
123.45 = 12345 ×10^{-2}
このように指数表記して、右辺の「+-」を符号部、「12345」を仮数部、(10の指数である)「-2」を指数部に格納する形になります。
[SAS] 情報落ち、対策の結果を実際に確認してみよう。
概要
(絶対値的に)差の大きい数値について、下記2点を実際に確認してみましょう。
SASで集計用データセットを作り、下記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_1 | sum_2 |
---|---|
11000000000000000 | 11000000000000002 |
集計元+集計過程 (work.data)
変数「id_1」と「sum_1」は、[情報落ち]分の“足し合わせ順”と“足し合わせ過程”、「id_2」と「sum_2」は[対策]分です。
[情報落ち]の足し合わせ過程(sum_1)を確認すると、小さい数の 1 が反映されていないことが分かります。
id_1 | id_2 | x | sum_1 | sum_2 |
---|---|---|---|---|
1 | 4 | 10000000000000000 | 10000000000000000 | 11000000000000002 |
2 | 1 | 1 | 10000000000000000 | 1 |
3 | 3 | 1000000000000000 | 11000000000000000 | 1000000000000002 |
4 | 2 | 1 | 11000000000000000 | 2 |