先頭へ戻る

数学関数 | Programming Place Plus C言語編 第48章

Programming Place Plus トップページ -- C言語編

先頭へ戻る

この章の概要

この章の概要です。


数学関数

C言語の標準ライブラリには、数学的な処理を行うための関数(以下、数学関数と表記)が多数含まれています。開発するプログラムの分野によっては、非常に有用になることもありますし、反対にほとんど用がないこともあるかもしれません。

この章では、分野を問わず使用頻度が高そうなものに限定して紹介します。ただし、数学的な解説はしません(そこは専門ではないので控えます)。また、数学関数を使う上で知っておくべきことについて触れます。

数学関数のほとんどは、<math.h> で宣言されています。ここにある関数は、引数や戻り値に float、double、long double のいずれかの型を使うようになっています。わずかながら整数型が使える数学関数がありますが、そういったものは <stdlib.h> の方に分離されています。

ほかに、math.h にある数学関数の複素数バージョンが <complex.h> にあります。

gcc や clang で <math.h> の関数を使うときには、コンパイルオプションに「-lm」を追加してください。数学関連のライブラリが分けられているため、このように明示的にリンクの指示を与える必要があります。

エラー処理

数学関数のいくつかは、計算が不可能であったり、結果が表現不可能であったりする可能性を持っています。このようなエラーが起きていることを検出する方法を知っておく必要があります。

数学関数が発生させるエラーには種類があります。

実引数の指定が適切な範囲内にない場合、定義域エラーが発生します。実例としては、平方根を求める sqrt関数後で取り上げます)において、実引数を負数にした場合があります。

結果が表現できない場合、値域エラー(「ちいき」と読みます)が発生します。値域エラーが発生する原因には、結果が巨大すぎて表現できないオーバーフローと、微小すぎて表現できないアンダーフローがあります。

実例として、べき乗を求める pow関数後で取り上げます)は値域エラーを発生させることがあります。

一般に、定義域エラーは、実引数を注意して指定することで防げます。各関数ごとに、定義域エラーを発生させない有効な値の範囲があるはずなので、その範囲を逸脱していないかどうか確認できるでしょう。たとえば、sqrt関数では、実引数を負数にしなければ、定義域エラーの発生を防げます。

一方で、値域エラーは、防ぐことが難しいこともあります。値域エラーの発生の有無は、関数の実装次第な部分もあるためです。

いずれにしても、注意して防ぐことが困難であるならば、適切な仕組みをもって検出する必要があります。数学関数のエラーは、errno の仕組みや、浮動小数点例外の仕組みを使って検出します。

どの仕組みが使われるかは、処理系によって異なります。math.h にある math_errhandling というマクロがどう置換されるかによって知ることができます。可能性は次の3通りです。

#define math_errhandling (MATH_ERRNO)
#define math_errhandling (MATH_ERREXCEPT)
#define math_errhandling (MATH_ERRNO | MATH_ERREXCEPT)

Visual Studio 2017、clang 5.0.0 での置換結果はいずれも、(MATH_ERRNO | MATH_ERREXCEPT) になっています。

置換結果に MATH_ERRNO が含まれている場合は errno の仕組みが使われ、MATH_ERREXCEPT が含まれている場合は、浮動小数点例外の仕組みが使われます。

errno

errno を使う場合は、定義域エラーの発生時には errno に EDOMが、値域エラーの発生時は ERANGEが格納されます。

EDOM も ERANGE も、その置換結果は 0 ではない値なので、エラーを errno で報告する標準ライブラリ関数を呼び出す際には、その呼び出しの直前で 0 を代入しておき、呼び出しの直後で値を調べるようにします。実際のプログラムは、この後、各関数を説明する中で掲載します。

浮動小数点例外

浮動小数点例外を使う場合は、数学関数がエラーを発生させたら、その内容に応じた浮動小数点例外を発生させます。浮動小数点例外が発生すると、浮動小数点状態フラグに、浮動小数点例外の種類に応じた値がセットされます。浮動小数点状態フラグは、直接的にはアクセスできないところにある変数のような存在です。

errno の仕組みが、数学関数以外でも使われることと同じく、浮動小数点例外の仕組みも数学関数以外でも使われます。たとえば、無限大から無限大を減算するような、通常の演算でも使用される可能性があります。

浮動小数点例外の種類に応じて、以下のマクロが <fenv.h> で定義されています。

マクロ 意味
FE_INVALID 定義域エラー。不正な演算を要求
FE_DIVBYZERO 値域エラー。結果が無限大になってしまう場合
FE_OVERFLOW 値域エラー。結果がオーバーフローしている
FE_UNDERFLOW 値域エラー。結果がアンダーフローしている
FE_INEXACT 結果が精度の問題で正確に表現できず、丸められている
FE_ALL_EXCEPT 上記のうち、処理系が対応しているものすべての組み合わせ

ただし、処理系がすべての浮動小数点例外に対応している保証はなく、一部のみの対応である可能性があります。いずれにしても、FE_ALL_EXCEPT は、対応しているすべての浮動小数点例外の組み合わせを意味します。

FE_INEXACT については、浮動小数点数の計算で丸めが起こるのはよくあることなので、わりと至るところで発生します。エラーというよりは、報告という感覚で受け取った方がいいかもしれません。

実際にエラーの有無を調べる流れは、errno を使ったものに似ています。

手順 errno 浮動小数点例外
errno に 0 を代入 feclearexcept関数を呼び出して、浮動小数点状態フラグをクリアする
数学関数を呼び出す 数学関数を呼び出す
errno の値を調べる fetestexcept関数を呼び出して、浮動小数点状態フラグの状態を調べる

feclearexcept関数と fetestexcept関数は、fenv.h に以下のように宣言されています。

int feclearexcept(int excepts);
int fetestexcept(int excepts);

いずれも実引数には、先ほどのマクロのいずれか、あるいは組み合わせを指定します。普通は、feclearexcept関数の方は FE_ALL_EXCEPT を指定して、すべてのフラグをクリアすることになるでしょう。

実引数に複数のマクロを指定するときにはビット和演算(第49章)を行います。

fetestexcept関数は、指定した浮動小数点例外のうちのいずれか1つでも発生していたら、0以外の値を返します。

正確にいえば、戻り値は、浮動小数点状態フラグと実引数とのビット積演算(第49章)を行った結果です。

なお、ここまでに取り上げた浮動小数点例外に関する機能を使うには、FENV_ACCESS という標準プラグマを、次のように定義する必要があります。

#pragma STDC FENV_ACCESS on

ただし、Visual Studio 2017 は標準プラグマに対応していないため、次のように Visual Studio の独自のプラグマを定義します。

#pragma fenv_access (on)

以下、sqrt関数(後述)で定義域エラーの発生を調べる例です。

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

#if defined(_MSC_VER)  // Visual Studio であるか?
#pragma fenv_access (on)
#else
#pragma STDC FENV_ACCESS on
#endif

void sqrt_test(double x)
{
    feclearexcept( FE_ALL_EXCEPT );
    double result = sqrt( x );
    if( fetestexcept(FE_INVALID) ){
        puts( "定義域エラーが発生しました。" );
    }
    else{
        printf( "%f\n", result );
    }
}

int main(void)
{
    sqrt_test( 9.0 );    // OK
    sqrt_test( -9.0 );   // 定義域エラー
    sqrt_test( 0.0 );    // OK

    return 0;
}

実行結果:

3.000000
定義域エラーが発生しました。
0.000000

C11 (極エラー)

C11 では、極エラー (Pole Error) が追加されています。これは、実引数が有限ではあるものの極限に近いために、計算結果が無限大になることで発生します。

極エラーの扱いは、値域エラーと同様です。

エラー処理に errno を用いる場合は、ERANGE がセットされます。

浮動小数点例外を用いる場合は、FE_DIVBYZERO で表される例外が発生します。


絶対値

絶対値を求めるには、fabsf関数fabs関数fabsl関数を使います。

float fabsf(float x);
double fabs(double x);
long double fabsl(long double x);

実引数に指定した値の絶対値を返します。非常に単純な関数であり、エラーを発生させることもありません。

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

int main(void)
{
    printf( "%f\n", fabs(1.0) );
    printf( "%f\n", fabs(-1.0) );
    printf( "%f\n", fabs(0.0) );

    return 0;
}

実行結果:

1.000000
1.000000
0.000000

また、<stdlib.h> に、整数型を扱う abs関数labs関数llabs関数があります。

int abs(int i);
long int labs(long int i);
long long int llabs(long long int i);

こちらも、実引数に指定した値の絶対値を返します。

整数型で表現できる値の範囲の都合上、最も小さい負数の絶対値を表現できない可能性があります。この場合に返される結果は未定義です。

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

int main(void)
{
    printf( "%d\n", abs(1) );
    printf( "%d\n", abs(-1) );
    printf( "%d\n", abs(INT_MIN) );      // 未定義の結果を返す可能性がある
    printf( "%d\n", abs(INT_MIN + 1) );

    return 0;
}

具体的には、数の表現に、2の補数表現を使う環境で問題になります。この表現方法では、負数の方が正数よりも 1つだけ多くの数を扱えます(たとえば -128~+127のように)。2の補数表現は非常に一般的であり、ほとんどの環境がこの表現方法を使っています(「コンピュータサイエンス編基数」のページを参照)

べき乗

べき乗を求めるには、powf関数pow関数powl関数を使います。

float powf(float x, float y);
double pow(double x, double y);
long double powl(long double x, long double y);

x の y乗を計算して返します。

これらの関数は、以下のようにエラーを発生させることがあります。

  1. x が負数で、y が整数でない場合、定義域エラー
  2. x が 0 で、y が 0 以下の場合、定義域エラー。あるいは値域エラー
  3. 結果が表現できない場合、値域エラー

【上級】処理系が IEC 60559 の仕様に準拠している場合、さらに細かな規定が追加されています。準拠の状況は、__STDC_IEC_559__マクロが定義されているかどうかで判定できます。

2番目が曖昧ですが、結果が無限大になってしまうようなケースで値域エラーになります。また、この条件を見る限りでは、x と y がともに 0 の場合もエラーになるはずですが、実際にはエラーにならず、1.0 を返す実装もあります(Visual Studio、clang はともに 1.0 を返します)。

C11 では、x が 0 で、y も 0 の場合は定義域エラー。x が 0 で、y が 0未満の場合は、定義域エラーまたは極エラーになるとされています。

動作を確認してみましょう。

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

void pow_test(double x, double y)
{
    errno = 0;
    double result = pow( x, y );
    if( errno == EDOM ){
        puts( "定義域エラーが発生しました。" );
    }
    else if( errno == ERANGE ){
        puts( "値域エラーが発生しました。" );
    }
    else{
        printf( "%f\n", result );
    }
}

int main(void)
{
    pow_test( 2.0, 3.0 );       // OK
    pow_test( 2.0, 0.0 );       // OK (0以外を 0乗すると、つねに 1.0)
    pow_test( 0.0, 2.0 );       // OK (0 を n乗すると、つねに 0.0)
    pow_test( 2.0, -3.0 );      // OK
    pow_test( -2.0, 3.0 );      // OK
    pow_test( 0.0, 0.0 );       // 定義域エラー (1.0 のこともある)
    pow_test( 0.0, -2.0 );      // 値域エラー
    pow_test( -2.0, 1.5 );      // 定義域エラー
    pow_test( DBL_MAX, 2.0 );   // 値域エラー
    pow_test( DBL_MIN, -2.0 );  // 値域エラー

    return 0;
}

実行結果:

8.000000
1.000000
0.000000
0.125000
-8.000000
1.000000
値域エラーが発生しました。
定義域エラーが発生しました。
値域エラーが発生しました。
値域エラーが発生しました。

平方根

平方根を求めるには、sqrtf関数sqrt関数sqrtl関数を使います。

float sqrtf(float x);
double sqrt(double x);
long double sqrtl(long double x);

x の平方根を返します。たとえば、9 の平方根には +3 と -3 がありますが、常に正の平方根が返されます。

これらの関数は、実引数が負数の場合、定義域エラーを発生させます。

動作を確認してみましょう。

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

void sqrt_test(double x)
{
    errno = 0;
    double result = sqrt( x );
    if( errno == EDOM ){
        puts( "定義域エラーが発生しました。" );
    }
    else{
        printf( "%f\n", result );
    }
}

int main(void)
{
    sqrt_test( 9.0 );    // OK
    sqrt_test( -9.0 );   // 定義域エラー
    sqrt_test( 0.0 );    // OK

    return 0;
}

実行結果:

3.000000
定義域エラーが発生しました。
0.000000

近い整数を得る(丸め関数)

小数点以下を切り上げたり、切り捨てたりした結果が欲しい場面があります。このような場合、ある浮動小数点数から、一番近い整数を得る関数が利用できます。

ceil系の関数は、実引数の値の小数点以下を切り上げた結果を返します。ceilf関数ceil関数ceill関数があります。

float ceilf(float x);
double ceil(double x);
long double ceill(long double x);

結果の型は整数型ではなく、引数と同じ型で返されます。また、これらの関数はエラーを発生させることはありません。

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

int main(void)
{
    printf( "%f\n", ceil(10.1) );
    printf( "%f\n", ceil(10.9) );
    printf( "%f\n", ceil(-10.1) );
    printf( "%f\n", ceil(-10.9) );

    return 0;
}

実行結果:

11.000000
11.000000
-10.000000
-10.000000

floor系の関数は、実引数の値の小数点以下を切り捨てた結果を返します。floorf関数floor関数floorl関数があります。

float floorf(float x);
double floor(double x);
long double floorl(long double x);

結果の型は整数型ではなく、引数と同じ型で返されます。また、これらの関数はエラーを発生させることはありません。

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

int main(void)
{
    printf( "%f\n", floor(10.1) );
    printf( "%f\n", floor(10.9) );
    printf( "%f\n", floor(-10.1) );
    printf( "%f\n", floor(-10.9) );

    return 0;
}

実行結果:

10.000000
10.000000
-11.000000
-11.000000

roundf関数round関数roundl関数は、実引数の値を小数点以下で四捨五入した結果を返します。一番近い2つの整数値のちょうど中間の値(つまり、4.5 とか -4.5 のような値)の場合は、0 から遠い方の値を返すことになっています(つまり、4.5 なら 5.0 が、-4.5 なら -5.0 が返されます)。

float roundf(float x);
double round(double x);
long double round(long double x);
#include <stdio.h>
#include <math.h>

int main(void)
{
    printf( "%f\n", round(10.1) );
    printf( "%f\n", round(10.9) );
    printf( "%f\n", round(-10.1) );
    printf( "%f\n", round(-10.9) );

    return 0;
}

実行結果:

10.000000
11.000000
-10.000000
-11.000000

lroundf関数lround関数lroundl関数 も round関数と同じことをしますが、結果を long int型で返します。便利そうな気もしますが、引数の型(浮動小数点数型)で表現できる値が、必ずしも long int型で表現できるとは限らないことに注意が必要です。戻り値の型で表現できない場合の結果は未規定です。また、実引数の絶対値が大きすぎると、値域エラーを発生させます。

long int lroundf(float x);
long int lround(double x);
long int lround(long double x);
#include <stdio.h>
#include <math.h>

int main(void)
{
    printf( "%ld\n", lround(10.1) );
    printf( "%ld\n", lround(10.9) );
    printf( "%ld\n", lround(-10.1) );
    printf( "%ld\n", lround(-10.9) );

    return 0;
}

実行結果:

10
11
-10
-11

llroundf関数llround関数llroundl関数 は、戻り値型が long long int型であること以外は、先ほどの lround系の関数と同様です。

long long int llroundf(float x);
long long int llround(double x);
long long int llround(long double x);

truncf関数trunc関数truncl関数は、小数点以下を捨て去った結果を返します(規格のとおりにいうと、絶対値が実引数の絶対値よりも大きくない値を返します)。たとえば、floor関数では -4.5 に対して、-5.0 を返しますが、trunc関数は -4.0 を返します。

float truncf(float x);
double trunc(double x);
long double truncl(long double x);
#include <stdio.h>
#include <math.h>

int main(void)
{
    printf( "%f\n", trunc(10.1) );
    printf( "%f\n", trunc(10.9) );
    printf( "%f\n", trunc(-10.1) );
    printf( "%f\n", trunc(-10.9) );

    return 0;
}

実行結果:

10.000000
10.000000
-10.000000
-10.000000

nearbyintf関数nearbyint関数nearbyintl関数は、現在の丸め方向に従って、小数点以下を丸めた整数値を返します。丸め方向は、浮動小数点数の演算の際、精度上表現できない値になったときに、どのような処置を取るかを決定するものです。これは、<fenv.h> にある関数やマクロによって変更・取得できます。

float nearbyintf(float x);
double nearbyint(double x);
long double nearbyintl(long double x);
#include <stdio.h>
#include <math.h>

int main(void)
{
    printf( "%f\n", nearbyint(10.1) );
    printf( "%f\n", nearbyint(10.9) );
    printf( "%f\n", nearbyint(-10.1) );
    printf( "%f\n", nearbyint(-10.9) );

    return 0;
}

実行結果:

10.000000
11.000000
-10.000000
-11.000000

rintf関数rint関数rintl関数は、nearbyint関数と同じことをしますが、結果が実引数の値と一致することを期待しています。つまり、実引数がすでに整数値になっていることを求めており、そうでない値を渡すと、FE_INEXACT浮動小数点例外を発生させることがあります。

float rintf(float x);
double rint(double x);
long double rintl(long double x);
#include <fenv.h>
#include <stdio.h>
#include <math.h>

#if defined(_MSC_VER)
#pragma fenv_access (on)
#else
#pragma STDC FENV_ACCESS ON
#endif

void call_rint(double x)
{
    feclearexcept(FE_ALL_EXCEPT);

    double result = rint(x);
    printf("%f%s\n",
        result,
        fetestexcept(FE_INEXACT) ? " : FE_INEXACT" : ""
    );
}

int main(void)
{
    call_rint(10.0);    // この場合、結果も 10.0 なので、nearbyint とまったく同じ
    call_rint(10.1);    // この場合、10.1 が 10.0 に変換される。一致しないので FE_INEXACT が発生するかもしれない
    call_rint(10.9);    // 同上
    call_rint(-10.1);   // 同上
    call_rint(-10.9);   // 同上

    return 0;
}

実行結果:

10.000000
10.000000 : FE_INEXACT
11.000000 : FE_INEXACT
-10.000000 : FE_INEXACT
-11.000000 : FE_INEXACT

lrintf関数lrint関数lrintl関数は、rint系の関数と同じことをしますが、結果を long int型で返します。戻り値の型で表現できない場合の結果は未規定です。また、実引数の絶対値が大きすぎると、値域エラーを発生させます。

long int lrintf(float x);
long int lrint(double x);
long int lrintl(long double x);

llrintf関数llrint関数llrintl関数は、戻り値型が long long int型であること以外は、先ほどの lrint系の関数と同様です。

long long int llrintf(float x);
long long int llrint(double x);
long long int llrintl(long double x);

対数

logf関数log関数logl関数は、自然対数(底がネイピア数 e の対数)を求めます。

float logf(float x);
double log(double x);
long double logl(long double x);

log10f関数log10関数log10l関数は、常用対数(底が 10 の対数)を求めます。

float log10f(float x);
double log10(double x);
long double log10l(long double x);

いずれの関数も、実引数が負数の場合、定義域エラーが発生します。また、実引数が 0 の場合には、値域エラーが発生することがあります。

【上級】処理系が IEC 60559 の仕様に準拠している場合、さらに細かな規定が追加されています。準拠の状況は、__STDC_IEC_559__マクロが定義されているかどうかで判定できます。

log10関数の使用例として、10進数の桁数を調べる関数を作ってみます。

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

int get_digits(int n)
{
    if( n == 0 ){
        return 1;
    }

    errno = 0;
    double result = log10( abs(n) );
    assert( errno == 0 );

    return (int)result + 1;
}

int main(void)
{
    printf( "%d\n", get_digits(0) );
    printf( "%d\n", get_digits(1) );
    printf( "%d\n", get_digits(100) );
    printf( "%d\n", get_digits(10000000) );
    printf( "%d\n", get_digits(-1) );
    printf( "%d\n", get_digits(-100) );
    printf( "%d\n", get_digits(-10000000) );

    return 0;
}

実行結果:

1
1
3
8
1
3
8

常用対数を求めることは、すなわち「ある数が 10 の何乗であるか」ということですから、この性質を利用して、10進数の桁数を得られます。単純に使うと 1 小さい数が得られるので +1 する必要はあります(たとえば、10 が 10 の 1乗であることを考えれば、意味が分かるでしょう)。

log10関数の実引数は 1以上にしないとエラーが発生してしまう可能性があるため、0以下の数の桁数を得たいときに対する備えが必要です。負数への備えとしては、絶対値を渡すようにしておけばよいです。0 の場合は直接 1(桁) を返すことにしています。

三角関数

三角関数についても用意されています。

正弦は sinf関数sin関数sinl関数で求められます。

余弦は cosf関数cos関数cosl関数で求められます。

正接は tanf関数tan関数tanl関数で求められます。

float sinf(float x);
double sin(double x);
long double sinl(long double x);

float cosf(float x);
double cos(double x);
long double cosl(long double x);

float tanf(float x);
double tan(double x);
long double tanl(long double x);

引数 x には、角度をラジアン単位で指定します。

「度」と「ラジアン」を変換するような関数やマクロは標準にはありません。必要があれば、自前で作っておくとよいでしょう。円周率の定義も標準にはないので、自前での定義が必要です。

円周率に関して、M_PI という名前の定義をよく見かけますが、これは標準のものではありません。

次のサンプルプログラムでは、45°刻みで、sin、cos、tan の結果を出力しています。

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

#define PI 3.14159265358979323846
#define DEG_TO_RAD(deg)  ((deg) / 180.0 * (PI))  // 度からラジアンへの変換

int main(void)
{
    double deg = -180.0;
    for( int i = 0; i <= 8; ++i ){
        printf( "sin(%.1f) = %f\n", deg, sin(DEG_TO_RAD(deg)) );
        deg += 45.0;
    }

    deg = -180.0;
    for( int i = 0; i <= 8; ++i ){
        printf( "cos(%.1f) = %f\n", deg, cos(DEG_TO_RAD(deg)) );
        deg += 45.0;
    }

    deg = -180.0;
    for( int i = 0; i <= 8; ++i ){
        printf( "tan(%.1f) = %f\n", deg, tan(DEG_TO_RAD(deg)) );
        deg += 45.0;
    }

    return 0;
}

実行結果:

sin(-180.0) = -0.000000
sin(-135.0) = -0.707107
sin(-90.0) = -1.000000
sin(-45.0) = -0.707107
sin(0.0) = 0.000000
sin(45.0) = 0.707107
sin(90.0) = 1.000000
sin(135.0) = 0.707107
sin(180.0) = 0.000000
cos(-180.0) = -1.000000
cos(-135.0) = -0.707107
cos(-90.0) = 0.000000
cos(-45.0) = 0.707107
cos(0.0) = 1.000000
cos(45.0) = 0.707107
cos(90.0) = 0.000000
cos(135.0) = -0.707107
cos(180.0) = -1.000000
tan(-180.0) = 0.000000
tan(-135.0) = 1.000000
tan(-90.0) = -16331239353195370.000000
tan(-45.0) = -1.000000
tan(0.0) = 0.000000
tan(45.0) = 1.000000
tan(90.0) = 16331239353195370.000000
tan(135.0) = -1.000000
tan(180.0) = -0.000000

90°のときの正接は定義できないため、おかしな結果になっていますが、エラーとはなりません。


<tgmath.h>

<math.h> や <complex.h> の数学関数は、型に応じた使い分けが必要です。特に、後から扱う型を変更した場合、呼び出す関数も修正しなければならないことに注意が必要です。暗黙的に型変換できてしまうため気付きづらく、意図しない精度で計算が行われてしまう可能性があります。

そこで、<tgmath.h> という標準ヘッダに、型総称マクロと呼ばれる特殊なマクロが定義されており、型の種類 (float、double、long double と、それぞれの複素数型のみ)を問わずに、同じ関数名が使えるようになっています。

たとえば、次のサンプルプログラムのように、float、double、long double のいずれの型でも「fabs」という名前で使用できます。

#include <stdio.h>
#include <tgmath.h>

int main(void)
{
    printf( "%f\n", fabs(-12.34f) );
    printf( "%f\n", fabs(-12.34) );
    printf( "%Lf\n", fabs(-12.34L) );

    return 0;
}

実行結果:

12.340000
12.340000
12.340000

<tgmath.h> は、Visual Studio 2017 では使用できません。

【上級】型総称マクロは、処理系がこれを特別扱いして「うまく処理」しているだけであり、我々はこの機能を真似できません。C11 からは、総称選択という新機能が追加されたことにより、真似することができるようになりました。


練習問題

問題① 2つの浮動小数点数が、どれだけ離れているかを計算するプログラムを作成してください。

問題② 2次元平面上にある2つの点の間の距離を計算するプログラムを作成してください。


解答ページはこちら

参考リンク


更新履歴

'2019/2/12 VisualStudio 2015 の対応終了。

'2018/5/25 新規作成。



前の章へ (第47章 ワイド文字)

次の章へ (第49章 ビット演算)

C言語編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
Twitter でツイート Twitter をフォロー LINE で送る
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー