「小数点以下の桁数を指定して切り捨てを行なう方法」が上手く行かなかった話 - SAS

小数点以下の桁数を指定して切り捨てを行なう方法」がSAS社のQAにあったので試したところ、上手く行かなかった。 大旨、うまくいくのだけれど、一部の値がおかしな結果になるみたい。


サンプルプログラム1

data test ;
  input a ;
cards ;
10
10.1
10.12
10.25
10000
123.123
9.9
9.99999
-100
-10.1
-1.12
-1.000
260.9
260.4
run ;

data _null_ ;
  set test ;
  b = a * 100 ;
  c = int(b) ;
  d = c / 100 ;
  putlog a= b= c= d= ;
run ;

実行ログ1

a=10 b=1000 c=1000 d=10
a=10.1 b=1010 c=1010 d=10.1
a=10.12 b=1012 c=1012 d=10.12
a=10.25 b=1025 c=1025 d=10.25
a=10000 b=1000000 c=1000000 d=10000
a=123.123 b=12312.3 c=12312 d=123.12
a=9.9 b=990 c=990 d=9.9
a=9.99999 b=999.999 c=999 d=9.99
a=-100 b=-10000 c=-10000 d=-100
a=-10.1 b=-1010 c=-1010 d=-10.1
a=-1.12 b=-112 c=-112 d=-1.12
a=-1 b=-100 c=-100 d=-1
a=260.9 b=26090 c=26089 d=260.89
a=260.4 b=26040 c=26039 d=260.39

見事に誤差がでてますね。ちなみに、int を floor に変更しても同じ結果。 切り捨てるどころか、むしろ小数桁を増やしてます(´・ω・`) どうも100倍した時点で、期待通りの値になっていないぽいです。


サンプルプログラム2

data _null_ ;
  a = 260.4 ;
  b = a * 100 ;
  c = 26040 ;
  putlog b= c= ;
  if a = b then putlog "同じだよ!" ;
           else putlog "別者だよ!" ;
  bx = put(b, hex16.) ;
  cx = put(c, hex16.) ;
  putlog bx= cx= ;
run ;

実行ログ2

b=26040 c=26040
別者だよ!
bx=40D96DFFFFFFFFFF cx=40D96E0000000000

見た目、同じ値なのですが、内部的な値は別者です。 なので、int したときに、違う値になってしまったのですね。 対策としては、「背筋が凍る、厄介でおっかない小数点誤差の話」で紹介されてるように、適当な値で丸める方法くらいしかないぽい?


サンプルプログラム3

data test ;
  input a ;
cards ;
260.9
260.4
run ;
data _null_ ;
  set test ;
  b = a * 100 ;
  b = round(b, 0.00001) ;
  c = int(b) ;
  d = c / 100 ;
  putlog a= b= c= d= ;
run ;

実行ログ3

a=260.9 b=26090 c=26090 d=260.9
a=260.4 b=26040 c=26040 d=260.4

これでうまくいきそうですけど、もっとスマートな方法はないものですかね・・・。

コメント

このブログの人気の投稿

日付フォーマットでない文字項目をSAS日付に変換するときにログ出力されるメッセージを抑制したい - SAS

データセット(.sas7bdatファイル)の文字コードを取得したい - SAS

Linuxコマンド: date で◯か月前 / ◯か月後を取得するときの注意