ソフトウェア

電卓アプリを作るのはなぜ難しいのか、Androidの「電卓」に施された工夫とは?


スマートフォンやPCには電卓アプリが搭載されており、簡単な計算であれば瞬時に答えを求めることができます。しかし、正確な答えを表示できるような電卓を開発するのは非常に難しく、その一例としてAndroidに搭載されている「電卓」アプリはどのような工夫で開発されたのかについて、エンジニアのチャド・ナウセアム氏が解説しています。

calculator-app - Chad Nauseam Home
https://chadnauseam.com/coding/random/calculator-app

iOSの「計算機」で、「(10100)+1-(10100)」を計算したところが以下。本来であれば、10100が打ち消されるので答えは「1」になるはず。しかし、計算機による計算結果は「0」となっており、間違いを示しています。


Androidの「電卓」で同じ計算を行ったところ、答えは正解の「1」を示しました。


電卓はただ数を計算するだけなので簡単だと思われがちですが、コンピューター上では浮動小数点数演算が行われるという点があるため、正確な計算には困難が伴います。10進数の数をIEEE 754規格の64bit倍精度浮動小数点数で表現する場合、「正負の符号(1bit)+仮数部(11bit)+指数部(52bit)」に分けることになるため、64bit倍精度浮動小数点数で表現できる数値は離散的になり、2進数で正確に表現できない場合は最も近い表現可能な値に丸められます。その結果、0.1のような単純な数値でも正確に表現できないことがあります。

例えば、以下はPython 3.12.3のコンソールで0.1+0.2の計算を行ったところ。答えは0.3なのですが、計算結果は「0.30000000000000004」と示されています。


Androidの「電卓」は、C/C++用のガベージコレクターとして知られる「Boehm Garbage Collector」の開発者であるハンス・ベーム氏によって設計されています。ベーム氏は「電卓」アプリを開発する上で、より正確に計算するためにはどうしたらいいのかを考えました。

より正確に計算するための方法の1つは「bignums」という任意精度整数を扱うためのデータ型を使う「有理数演算」です。bignumsは通常の数値型とは異なり、サイズに制限がない整数を表現できるため、10100といった巨大な数値も正確に扱うことが可能。例えば、bignumsを使えば、分子と分母それぞれにbignumsを使用することで分数の計算が簡単にできます。これにより、有理数を正確に実装することが可能になります。


しかし、「有理数演算」では分数で表せないような数、すなわちπや√2のような無理数は表現できません。このうち、√2は「x2-2=0」という有利係数の多項式の解という形で表現できるため、数値を多項方程式として表現する「代数的演算」が利用できます。しかし、πは超越数であるため、有利係数の多項式で表現することはできません。

この問題を解決できるのが、精度となる有効数字を設定し、その精度を満たす有理数を出力する「再帰的実数演算」という考え方です。

例えば、再帰的実数演算でπを求める場合、「誤差0.01以内の精度でπを求めよ」という要求に対しては3.14を返し、「誤差0.001以内の精度でπを求めよ」という要求に対しては3.141を返します。


さらに、誤差0.01以内の精度で2πを求めたい場合、π×2という乗算だと誤差も2倍になります。そのため、πは誤差0.005以内の精度となる3.141で計算される必要があり、これを2倍することで、誤差0.01以内の精度となる2π=6.282を返すことになります。

再帰的実数演算を使えば無理数や超越数でも計算することが可能になりますが、あくまでも指定した誤差の範囲内で近似しているだけなので、どうしても誤差が生じてしまいます。そのため、例えば「1-1=0.00000000000000000」という計算結果が出力されてしまったり、sinπ=0.000000000000000と表示してしまったりする問題があります。


そこで、ベーム氏は「電卓」アプリを開発する上で、有理数演算と再帰的実数演算を組み合わせました。整数を使った単純な計算の時は有理数演算だけを使うので、正確な答えがすぐに得られる一方で、πや√2が含まれる計算の時は再帰的実数演算を使って近似計算を行います。これにより、「1-1」は完全な0として表示でき、「1+e^(-1000)-1」は小さな正の数として表示できるという、実用的な電卓に必要な両方の要件を満たすことが可能になりました。

さらにベーム氏は「arg」を有理数の引数とした時に、πarg、√arg、earg、ln(arg)、log10(arg)、sin(πarg)、tan(πarg)というように、数値を関数として表現するシンボリック表現を導入しています。これにより、πやeを無限小数ではなく記号として扱えるようになり、sinπを0と出力することができるというわけです。


例えば、1×π+3×πという計算を行う場合、1×πや3×πはシンボリック表現として扱えるため、1+3という有理数部分だけを演算し、4πというシンボリック表現が得られます。こうすることで、可能な限り正確な計算を維持しながら、実用的な計算速度を実現することが可能になりました。

ナウセアム氏は「次にAndroidの電卓を使う時は、もう少しその価値を理解していただければ幸いです」とコメントしました。なお、ベーム氏は開発に際して自身が研究した内容を以下の論文にまとめて発表しています。

Towards an API for the Real Numbers
https://research.google/pubs/towards-an-api-for-the-real-numbers/

この記事のタイトルとURLをコピーする

・関連記事
iOS 11の計算機に「1+2+3」の答えを「24」と表示するバグの存在が判明 - GIGAZINE

遊園地経営SLGのジェットコースターで計算機を作り上げた猛者が登場 - GIGAZINE

Windows 10搭載の古いPCで「電卓」や「フォト」などが起動しなくなる不具合が発生 - GIGAZINE

MicrosoftがWindows 10標準の「Windows 電卓」をオープンソース化 - GIGAZINE

スマブラを電卓上で再現してしまった猛者が登場 - GIGAZINE

in ソフトウェア, Posted by log1i_yk

You can read the machine translated English article Why is it difficult to create a calculat….