マイコンでtan(355/226)の計算

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-CITRUSArduino 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型)で計算している以上、計算結果の差はマイコンアーキテクチャによるものではなく、使用しているコンパイラ(というかライブラリのアルゴリズム)によるもの、ということでしょう。