絶対値を求める | Programming Place Plus C言語編 逆引き

トップページC言語編逆引き

このページの概要

以下は目次です。

目的

ある数値の絶対値を求めたいとします。

絶対値は 0 からの距離です。+3 の絶対値は 3 ですし、-3 の絶対値も 3 です。

方法①(標準ライブラリ関数を使う)

絶対値を求める関数は標準に存在していますから、これを使えばいいです。ただし、型の違いによる使い分けが必要です。また、インクルードすべき標準ヘッダも異なります。

【上級】これらのほかに、複素数型を扱う関数が complex.h にあります。また、math.h と complex.h の関数については、型の違いによらず同じ名前が使えるようにする型総称マクロが tgmath.h に定義されています。

いずれの関数も、絶対値を求めたい値を実引数にして呼び出せば、戻り値で結果が返ってきます。

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

int main(void)
{
    printf("%d\n",           abs(100));
    printf("%ld\n",          labs(100L));
    printf("%lld\n",         llabs(100LL));
    printf("%f\n",           fabsf(12.3f));
    printf("%lf\n",          fabs(12.3));
    printf("%Lf\n",          fabsl(12.3L));
    printf("%" PRIdMAX "\n", imaxabs(INTMAX_C(100)));

    puts("---");

    printf("%d\n",           abs(-100));
    printf("%ld\n",          labs(-100L));
    printf("%lld\n",         llabs(-100LL));
    printf("%f\n",           fabsf(-12.3f));
    printf("%lf\n",          fabs(-12.3));
    printf("%Lf\n",          fabsl(-12.3L));
    printf("%" PRIdMAX "\n", imaxabs(INTMAX_C(-100)));
}

実行結果:

100
100
100
12.300000
12.300000
12.300000
100
---
100
100
100
12.300000
12.300000
12.300000
100

絶対値を取得する際には1点、重大な落とし穴があります。

数を表現するしくみ上、ある型で表現できる一番小さい数(つまり、表現できる負数の中で一番小さい数)の絶対値は、表現できない可能性があるということです。

ここで取り上げた標準の関数の場合、結果が表現できないときには、未定義の動作となります。つまり、そのような値を渡さないようにする責任は我々の側にあります。

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

たとえば、int型で表現できる一番小さい数は INT_MINマクロで、一番大きい数は INT_MAXマクロで得られますが、abs(INT_MIN) とした結果は表現できない可能性があります。

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

int main(void)
{
    printf("%d\n", abs(INT_MIN));  // 未定義の動作になり得る
}

方法②(自力で計算する)

絶対値は、元の数が正の数ならそのまま、負の数なら符号を反転する、という単純な操作で求められます。そのため、自力で実装してしまうことも考えられます。

#include <stdio.h>

int main(void)
{
    int v1 = 100;
    int v2 = -100;

    printf("%d\n", v1 >= 0 ? v1 : -v1);
    printf("%d\n", v2 >= 0 ? v2 : -v2);
}

実行結果:

100
100

この方法を使う場合も、方法① で取り上げたとおり、表現できる一番小さい数の絶対値は表現できない可能性があることに注意してください。

この方法を使うのなら、関数形式マクロ(第28章)にして用意しておけば、ほかの型でもそのまま使える利点があります。

#define ABS(v)  ((v) >= 0 ? (v) : -(v))

関数でできることにマクロを持ち出すのは、あまり良い考えではありませんが(第28章)、このような絶対値マクロはよく使われています。

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>

#define ABS(v)  ((v) >= 0 ? (v) : -(v))

int main(void)
{
    printf("%d\n",           ABS(100));
    printf("%ld\n",          ABS(100L));
    printf("%lld\n",         ABS(100LL));
    printf("%f\n",           ABS(12.3f));
    printf("%lf\n",          ABS(12.3));
    printf("%Lf\n",          ABS(12.3L));
    printf("%" PRIdMAX "\n", ABS(INTMAX_C(100)));

    puts("---");

    printf("%d\n",           ABS(-100));
    printf("%ld\n",          ABS(-100L));
    printf("%lld\n",         ABS(-100LL));
    printf("%f\n",           ABS(-12.3f));
    printf("%lf\n",          ABS(-12.3));
    printf("%Lf\n",          ABS(-12.3L));
    printf("%" PRIdMAX "\n", ABS(INTMAX_C(-100)));
}

実行結果:

100
100
100
12.300000
12.300000
12.300000
100
---
100
100
100
12.300000
12.300000
12.300000
100


参考リンク


更新履歴

’2019/10/7 新規作成。



逆引きのトップページへ

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

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る