tan(355/226)の計算は関数電卓の正確さを試すベンチマークによく使われるらしいです。というのも、355/226はπ/2に極めて近い有理数であり、π/2近傍でtanは無限大に発散するからです。
下表のように両者は10進数で7桁まで一致しています。
π/2 | 1.5707963267948966192313216916398 |
355/226 | 1.5707964601769911504424778761062 |
tan(355/226) の真の値は、-7497258.185...ですが、この近傍でラジアン値の有効数字10桁目が1ズレるだけでtanの値は有効数字3桁目からズレてきます。ちなみに、Androidの関数電卓アプリの定番といえるRealCalcは正しい10桁の値を出しますが、市販の関数電卓では4桁〜7桁くらいの正確さになるのものが多いようです。
今回は、いろいろなマイコン/開発環境でtan(355/226)計算の正確さを試してみます。
Arduino Uno
マイコン | AVR ATmega328 |
コンパイラ | GCC |
計算結果 | -6135667.5000000000 |
正確さ(有効数字) | 0桁 |
残念な結果です。Arduino Unoでは、double型も実は単精度のfloat型で実装されているようです。float型は有効数字約7桁ですから、まあ妥当な結果と思います。Arduino UnoのCPUは8ビットのAVRマイコンですから、倍精度実数など期待する方がお門違いかもしれません。
誤解の無いように念のために言うと、これはArduino Unoの三角関数が使い物にならないという意味ではありません。π/2近傍のtanの値などというのは、実用上使わない(使えない)イジワルな計算であって、Arduino Unoが使われるたいていの用途においては、単精度の三角関数は十分な精度を持っていると思います。
実験に使ったソースを以下に示します。
void setup() { Serial.begin(115200); } void loop() { volatile double a = 355.0; volatile double b = 226.0; double x = tan(a/b); Serial.println(x,10); delay(1000); }
GR-CITRUS
マイコン | RX631 |
コンパイラ | GCC |
計算結果 | -7497258.1791403731 |
正確さ(有効数字) | 8桁 |
GR-CITRUSはArduino Pro Mini互換ボードですが、32ビットのRXマイコンなのでさすがにdouble型はちゃんと倍精度で実装されています。実験に使用したソースは Arduino Uno と同一です。
mbed LPC1768 (オンラインコンパイラでビルド)
マイコン | LPC1768 (ARM Cortex-M3) |
コンパイラ | MDK-ARM |
計算結果 | -7497258.1791403722 |
正確さ(有効数字) | 8桁 |
GR-CITRUSとは微妙に違う計算結果ですが、ほぼ同じ程度の正確さです。
実験に使ったソースを以下に示します。
#include "mbed.h" Serial pc(USBTX, USBRX); int main() { pc.baud(115200); while(1) { volatile double a = 355.0; volatile double b = 226.0; double x = tan(a/b); pc.printf("%.10f\n",x); wait(1); } }
mbed LPC1768 (LPC Xpresso/GCCでビルド)
マイコン | LPC1768 (ARM Cortex-M3) |
コンパイラ | GCC |
計算結果 | -7497258.1791403731 |
正確さ(有効数字) | 8桁 |
オンラインコンパイラからエクスポートしたプロジェクトをLPC Xpressoにインポートしてビルドし、実行してみました。ハードウェアが同じmbed LPC1768でも、GCCでビルドすると計算結果はGR-CITRUSと同じになりました。けっきょく、同じ倍精度の浮動小数点実数(double型)で計算している以上、計算結果の差はマイコンのアーキテクチャによるものではなく、使用しているコンパイラ(というかライブラリのアルゴリズム)によるもの、ということでしょう。