C言語で双曲線正弦関数の精度とパフォーマンスを追求:sinhl関数の代替方法を徹底比較


構文

double sinhl(long double x);

引数

  • x: 関数の引数となる値。long double 型の浮動小数点数を渡します。

戻り値

  • x の双曲線正弦を double 型の浮動小数点数値として返します。

エラー処理

  • 計算結果がアンダーフローする場合、sinhl 関数は 0 を返し、errnoERANGE に設定します。
  • x の絶対値が大きすぎる場合、sinhl 関数は errnoERANGE に設定し、x の符号に応じて ±HUGE_VAL を返します。

#include <stdio.h>
#include <math.h>

int main() {
  double pi = 3.1415926535;
  long double x = pi / 2;
  long double y = sinhl(x);

  printf("sinh(pi / 2) = %Lf\n", y);

  return 0;
}

このプログラムは、以下の出力を生成します。

sinh(pi / 2) = 2.302585094652362
  • C++ では、sinhl 関数はオーバーロードされており、float 型と double 型の引数も受け付けます。
  • sinhl 関数は、sinh 関数と似ていますが、引数と戻り値の型が long double 型になっています。これは、より高い精度が必要な場合に sinhl 関数を使用することを意味します。


双曲線正弦と通常の正弦の比較

この例では、sinhl 関数と sin 関数を使用して、同じ角度の双曲線正弦と通常の正弦を計算し、比較します。

#include <stdio.h>
#include <math.h>

int main() {
  double pi = 3.1415926535;
  double angle = pi / 4;

  double sinh_value = sinhl(angle);
  double sin_value = sin(angle);

  printf("sinh(%f) = %f\n", angle, sinh_value);
  printf("sin(%f) = %f\n", angle, sin_value);

  return 0;
}
sinh(0.785398) = 1.175201
sin(0.785398) = 0.707107

双曲線正弦関数のグラフ描画

この例では、sinhl 関数を使用して双曲線正弦関数のグラフを描画します。

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

#define X_MIN -5.0
#define X_MAX 5.0
#define Y_MIN -10.0
#define Y_MAX 10.0
#define STEPS 100

int main() {
  double x, y;
  double step = (X_MAX - X_MIN) / STEPS;

  // ターミナルのサイズを取得
  int width, height;
  getTerminalSize(&width, &height);

  // グラフのスケーリング
  double x_scale = width / (X_MAX - X_MIN);
  double y_scale = height / (Y_MAX - Y_MIN);

  // グラフを描画
  for (x = X_MIN; x <= X_MAX; x += step) {
    y = sinhl(x);

    // ターミナル座標に変換
    int x_term = (int)(x_scale * (x - X_MIN));
    int y_term = (int)(y_scale * (y - Y_MIN) + height - 1);

    // プロット
    printf("\x1b[%d;%dH*", y_term, x_term);
    printf("*");
  }

  return 0;
}

このプログラムを実行すると、以下のようになります。

双曲線正弦関数のグラフ

逆双曲線正弦の計算

この例では、asinh 関数を使用して逆双曲線正弦を計算します。asinh 関数は C言語標準ライブラリには含まれていないため、この例では math.h ヘッダーファイルに加えて cmath ヘッダーファイルもインクルードする必要があります。

#include <stdio.h>
#include <math.h>
#include <cmath>

int main() {
  double y = 2.0;
  double x = asinh(y);

  printf("asinh(%f) = %f\n", y, x);

  return 0;
}
asinh(2.000000) = 1.316958


exp 関数と cosh 関数の組み合わせ

sinhl 関数は、以下の式を使用して exp 関数と cosh 関数の組み合わせで実装できます。

double sinhl(double x) {
  return (exp(x) - exp(-x)) / 2;
}

この式は、双曲線正弦の定義に基づいています。

シリーズ展開

sinhl 関数は、以下の無限級数で表すことができます。

double sinhl(double x) {
  double result = 0;
  double term = x;
  int n = 1;

  while (fabs(term) > 1e-15) {
    result += term;
    term *= x * x / (2 * n * n + 1);
    n++;
  }

  return result;
}

この方法は、高い精度が必要な場合に役立ちますが、計算コストが高くなります。

ライブラリの利用

sinhl 関数は、C言語標準ライブラリに含まれていないため、他のライブラリから提供される同等の関数を使用することもできます。以下に、いくつかの例を紹介します。

  • MPFR
    mpfr.h ヘッダーファイルに mpfr_sinh 関数が定義されています。
  • Mathlib
    mathlib.h ヘッダーファイルに sinh_fast 関数が定義されています。
  • GNU C ライブラリ
    gmath.h ヘッダーファイルに sinh_l 関数が定義されています。

これらのライブラリは、標準ライブラリの sinhl 関数よりも高速で精度が高い場合がありますが、ライブラリのインストールと設定が必要になる場合があります。

最適な代替方法の選択

sinhl 関数の代替方法は、用途によって異なります。

  • 標準ライブラリよりも高速で精度が高い代替方法が必要な場合は、ライブラリを使用します。
  • 高い精度が必要な場合は、シリーズ展開を使用します。
  • 高速な計算が必要な場合は、exp 関数と cosh 関数の組み合わせが適しています。
  • 計算精度とパフォーマンスのトレードオフを考慮する必要があります。
  • 上記の代替方法は、すべて double 型の引数と戻り値を使用します。long double 型の引数と戻り値が必要な場合は、対応する関数を実装する必要があります。