「Windowsの標準電卓で4の平方根が2でなかった仕様がようやく修正」の件について

Windowsの標準電卓で4の平方根が2でなかった仕様がようやく修正」というニュース( pc.watch.impress.co.jp )がありました。現時点では私の手元にそんなに情報が無いので、

  • 以下で述べる内容
    • 以前の仕様のあらまし
    • 修正に関する憶測と心配事
  • 以下で述べない内容
    • 以前の仕様の正確な理由はなぜか
    • 具体的にどう修正されたのか

という感じになります。

以前の仕様について

プレスリリース( https://blogs.windows.com/windowsexperience/2018/04/04/announcing-windows-10-insider-preview-build-17639-for-skip-ahead/ )の中の Windows Calculator Improvements という節の中でリンクされているMSDNで開発者が書かれたブログエントリ( https://blogs.msdn.microsoft.com/oldnewthing/20160628-00/?p=93765 )と、そこからさらにリンクされている、もっと前のエントリ( https://blogs.msdn.microsoft.com/oldnewthing/20040525-00/?p=39193 )にあるように、大昔はもっとエエカゲンだった「電卓」ですが、近年は「an arbitrary-precision arithmetic library」により、「四則は無限精度」「それ以外の関数などは32桁の精度」で計算する、という仕様になっていました。

(明確には書かれていませんが、挙げられている例から見て、四則演算の間は除算の結果などは内部で分数として保持するようなライブラリ(すなわち、有理数演算の機能も含んでいるライブラリ)なのだと思われます)

そして、平方根は四則演算ではないので、有限精度で計算され、(後述の追記参照)、また、正の値であればどんな場合であれ全て exp(ln(x)/2) で計算していることから、√4 のような場合でも誤差が発生する場合がある(そして、それは気にしないでしょ?)、となっていました。

これ自体には問題はありません。コンピュータで以上のような計算のしかたをすれば、必ず起きる可能性があるものとして、受け取らねばならないものです。

なお、1.99999999999999999989317180305609 という数がどうやって出てくるのかについては、いくつか検討してみましたが、まだ糸口がつかめていません。(どうやら見つかりました。次の追記を参照)

追記: 平方根の計算に使っている ln と exp については、拡張浮動小数点演算を使っている(のだろう)と、小飼さん(@dankogai)から指摘がありました。十六進小数にすると 1.ffff_ffff_ffff_fffe0784a26b6c0c... ですので、上位の1の連続が、ちょうど拡張浮動小数点の64ビット(ケチ表現無し)と一致します。確かに!

憶測

以下は、これの修正に関する、あくまでも「憶測にもとづいた」議論です。具体的にどう修正されたのかがはっきりすれば、心配の必要は全てなくなります。

最も想像される修正

おそらく最も可能性が高いと思われるのは、入力の値が整数になっていて(これは前述のライブラリであれば、正確に判定できるはず)、整数平方根により正確な値が得られたならば(これも微妙な点は無いはず)、それを結果とする、という処理が、平方根の前処理として追加された、というもので、基本的に安全な範囲の修正と言えます。

確率は低いと思われるが、懸念される修正

以前のMicrosoftにはあった悪癖として、数値を扱うアプリでアドホックな手当をする、というものがありました(詳しくは、奥村先生が2001年2月に書かれた(と思われる)、「Excelの演算誤差」という記事( https://oku.edu.mie-u.ac.jp/~okumura/software/excel/roundoff.html )の、「恐ろしくて数値計算に使えたものではない。」と閉じられている最後の段落あたりを参照)。

元々のこの問題のチケットが、単に放置されていたのではなく、もし「WONTFIX」になっていたものを*1、わざわざ修正したのだとして、なぜ今更修正したのかがはっきりしてなかったとすると、「なんとなく直した」のではないかという懸念が出てきます。

アドホックに手当する、というのは、こういった問題で一番やってはいけないことで、もしそうだったら、という不安があります。

こうすればもっと良かったんじゃないかという気もする修正

徳丸さんのツイートによれば、

とのことなので、以下で私が考えるような「さいきょうの修正」は、されていない模様ですが、参考のために書いておきます。

平方根には「荒っぽい近似が得られれば、ごく簡単に精度の桁を倍近く伸ばせる」という性質があります。具体的には、ニュートン法の反復の1回の追加、すなわち:

  • 平方根を求めたい値を x
  • 得られている sqrt(x) の近似値を y

とした時、

  • y := (y + x/y) / 2.0

とすれば、1回でもかなり改善されます。また、見ての通りこの演算には四則演算しか使いませんから、Microsoftが「電卓」の実装に使っているライブラリで任意精度で計算できますので、これを1回(か2回)実行してから、例の「32桁」に四捨五入で丸めれば、

  • 全ての場合で精度が改善される
  • 問題だったような、「トリビアルに自明な結果」を外すことも無くなる(はず)

だと思うのですが、どうでしょうか。

*1:後述のように、平方根の計算には、簡単かつ確実に精度を上げる方法がありますから、それなりに意図的にそのままにしたものではないか、という気がします。