備忘ログ

チラシの裏的備忘録&メモ

Qittaの「あなたの平均値の計算、本当にできていますか?」をみて-Rでの整数の上限は上限が2147483647、下限が-2147483647で64bitの世界は見れなかった話-

qiita.com

Qittaのこの記事をみてRでは……という話をしたいだけ。

やってみたら「やったか……!?」と思ったけど、噴煙の中を覗いてみたら「な、なんだと!?」みたいな感じでできていなかったという感じ。

Rの整数型について調べてわかったことと、桁数の扱いがわかったのでメモ(途中にある)。

一応Rのバージョンが上がると状況変わる可能性もあるので、Rversion載せておく。

sessionInfo()[["R.version"]][["version.string"]]
## [1] "R version 4.0.2 (2020-06-22)"
sessionInfo()$platform
## [1] "x86_64-w64-mingw32/x64 (64-bit)"

できたか……!?

ところでそもそも、Rは標準状態だと

1234567890123
## [1] 1.234568e+12

となり、ある程度の桁数になると浮動小数点表記法(floating point expression)になる。

表示の有効数字桁数をoptionでdigitsで表示の有効桁数設定を変えて見えるようにする。

最大値が22となている(23以上はエラーになる)。

# 表示可能な最大範囲で表示できるようにする。
options(digits = 22)
# もとに戻すならoptions(digdits = 7)とするか、Rを再起動する。
1234567890123
## [1] 1234567890123

これで準備完了。

Qittaで挙げられていた4611686018427387905と4611686018427387903の平均値について計算すると組み込みbaseの関数のmeanを使うと

mean(c(4611686018427387905, 4611686018427387903))
## [1] 4611686018427387904

となり、期待する値がでて「できたか……!?」となる(後述するができてないし、Qittaの言いたいこととちょっと違う)。

Qittaで挙げられていた回避策用の関数(実はちゃんとしていない、後述する)を作って計算してみる。

average <- function(x, y){
  x + ((y - x) / 2)
}
average(4611686018427387905, 4611686018427387903)
## [1] 4611686018427387904

となり、期待する値がでて「できたか……!?」となる(後述するが関数がQittaの意図するかたちでちゃんとしていない以前の問題でできてない)。

な、なんだって!?

4611686018427387909
## [1] 4611686018427387904
4611686018427387900
## [1] 4611686018427387904

ただ数字を出力しようとしてるのに、そのままの数字がでない\(^o^)/オワタ

なので、

mean(c(4611686018427387901, 4611686018427387902))
## [1] 4611686018427387904
average(4611686018427387909, 4611686018427387908)
## [1] 4611686018427387904

という結果になってしまう。

つまり、できたかと思っていたものもたまたまうまくいく数字の組み合わせだっただけでちゃんとできていないということ。

整数で入力したつもりが整数型を指定していないので内部で浮動小数点型なので意図した値が出ないということ。

[http://www.okadajp.org/RWiki/?Tips%2F%E6%9C%89%E5%8A%B9%E6%95%B0%E5%AD%97%E6%A1%81%E6%95%B0%E3%81%AE%E5%A4%89%E6%9B%B4:title] 曰く、15-16桁程度が意味のある最大桁数のようで、実際にいろいろみてみても16桁以降では表示数が意図するものとは異なることがある。 22桁まで表示できるが実用性は疑問符で、この有効数字の桁数について触れらている記事が2015年のものなのでバグというよりもはや仕様なのだろうと思う。

そもそも、もともとの記事で整数型をしていているので、整数型にしてやればいいんじゃないか(というか整数型でやるべき)なのだが、

as.integer(2147483647)
## [1] 2147483647
as.integer(2147483648)
## Warning: NAs introduced by coercion to integer range

## [1] NA

でQittaで触れている大きい数は整数型にならない(ここがオチ)。

integer Vectorsのドキュメントには

Note that current implementations of R use 32-bit integers for integer vectors

と書いてあるので、現在Rは整数型は32bitの範囲しか扱えないようなので、そもそも今回の64bitの範囲では溢れの件はRにはあたらないはなしだったということが最後のオチ。

そもそも32bitの世界で生きていたので64bitの整数世界にはたどり着けない話だった。

so the range of representable integers is restricted to about +/-2*109: doubles can hold much larger integers exactly.

と書いてあるが、doublesだとintegerじゃないじゃんと思ったけどよくわからない。

ちゃんと整数型で平均値を出す

Rユーザーは諦めて32bitの整数世界で生きていく。

2値を足して2で割る平均の関数をQittaの最初の例にならって作ると。

average_int <- function(x, y){
  x <- as.integer(x)
  y <- as.integer(y)
  res <-  (x + y) / 2
  return(res)
}

んでこれは小さい数字ならOKだが、桁があふれる値、整数型は32bitの+/-範囲なので、上限が2147483647、下限が-2147483647でこれを超えると整数型の上限を溢れてNAを返す。

average_int(1, 3)
## [1] 2
average_int(1073741823, 1073741825)
## Warning in x + y: 整数の桁あふれにより NA が生成されました

## [1] NA

Qittaで挙げられた回避策込みの整数型指定で2値を2で割る計算をつくる。Qittaのコメントにもあるけど、xが必ずyよりも小さい前提の関数(条件分岐で2値の順序なんていじれるけど、今回はQittaのまま再現する)。

average_int <- function(x, y){
  x <- as.integer(x)
  y <- as.integer(y)
  if(!is.na(x + y)) res <- (x + y) / 2
  else res <- x + ((y - x) / 2)
  return(res)
}

Rは整数型で桁あふれするとNAを返すのを利用して、if分岐している。

32bitなので、上限が2147483647、下限が-2147483647でこれを超えると整数型の上限を溢れてNAを返す。

これなら途中で上限超えても、超えない方でトライできないかもう一度やれる。

average_int(1073741823, 1073741825)
## Warning in x + y: 整数の桁あふれにより NA が生成されました

## [1] 1073741824

だからどうしたという話以上でもなんでもないけど。

Qittaの記事の内容をRで再現してみたという話。