閉じる

【C言語】absで絶対値を求める全パターン|マイナス判定から浮動小数まで

C言語で負の数を扱うとき、必ずと言ってよいほど登場するのが絶対値です。

標準ライブラリにはabsfabsといった便利な関数がありますが、型ごとに使い分けないと意図しない挙動やオーバーフローの原因になります。

本記事では、C言語で絶対値を求める全パターンを、整数・浮動小数・自作関数まで一通り整理して解説します。

abs関数とは

absとは|絶対値を求める標準関数

abs関数は、int型の整数の絶対値を返す標準関数です。

C言語の標準ヘッダであるstdlib.hに宣言されています。

abs関数は、引数が負の値なら符号を反転して正の値にし、引数が0または正の値ならそのまま返します。

数学的に言えば、整数版の|x|を計算する関数です。

より正確には、標準規格では次のように定義されています。

  • 引数xが0以上ならxを返す
  • 引数xが0より小さいなら-xを返す

ただし、後述するようにすべての整数で安全に符号反転できるとは限らないことがポイントになります。

absでできること・できないこと

まず、absで「できること」と「できないこと」を整理しておきます。

absでできることは、主に次のような処理です。

int型の範囲内であれば、負の整数を正の整数に変換して扱うことができます。

たとえば配列のインデックスなど、負になってほしくない場面で、値を絶対値にして使うといった用途が挙げられます。

一方で、absには明確な制限があります。

absはあくまで「int型」に対する関数なので、long, long long, float, doubleといった他の型の絶対値を求めるには別の関数が用意されています。

また、INT_MINの絶対値を安全に返すことは保証されていません

この点は、後の「0やINT_MINをabsに渡すときの注意点」で詳しく解説します。

absとfabs・labsなどの違い

C言語では、絶対値を求める関数が複数定義されています。

代表的なものを表にまとめます。

以下の表は、型と関数の対応を一覧にしたものです。

用途関数名ヘッダ
整数の絶対値intabsstdlib.h
整数の絶対値(長整数)longlabsstdlib.h
整数の絶対値(より長整数)long longllabsstdlib.h
実数の絶対値(倍精度)doublefabsmath.h
実数の絶対値(単精度)floatfabsfmath.h
実数の絶対値(拡張精度)long doublefabslmath.h

整数と浮動小数点数で関数が分かれていること、さらに整数の中でもサイズによって関数が異なることを理解しておくことが重要です。

また、fabsはdouble専用であり、floatlong doubleのときはfabsffabslを使うのが望ましいです。

Cでは実引数がfloatでも自動でdoubleに拡張されるため、fabsに渡しても動作はしますが、型の一貫性の面から<b>適切な関数を使い分けた方が可読性が高まります</b>。

absの基本的な使い方

absの書式とインクルードファイル

まずはabsの正式な宣言と必要なインクルードファイルを確認します。

absはstdlib.hで宣言されています

C言語
/* abs関数の宣言(標準ヘッダ内のイメージ) */
int abs(int x);

このため、プログラムでabsを使うときは#include <stdlib.h>を忘れずに記述する必要があります。

特に古いコンパイラや厳密な警告設定をしている環境では、インクルードを忘れると暗黙の宣言として扱われ、思わぬ不具合につながることがあります。

absを使う最小のサンプル

C言語
#include <stdio.h>
#include <stdlib.h>  /* absを使うために必要 */

int main(void) {
    int x = -10;
    int y = abs(x);  /* xの絶対値を計算 */

    printf("x = %d\n", x);
    printf("abs(x) = %d\n", y);

    return 0;
}
実行結果
x = -10
abs(x) = 10

このように、absの使い方は非常にシンプルで、負の整数を渡してその絶対値をintで受け取るだけです。

int型のマイナス値をabsで絶対値にする例

もう少し実用に近い例として、いくつかの値をループで処理してみます。

C言語
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    /* 負の値と正の値が混ざった配列 */
    int values[] = { -3, -1, 0, 2, 5 };
    int size = sizeof(values) / sizeof(values[0]);

    printf("元の値: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", values[i]);
    }
    printf("\n");

    printf("絶対値: ");
    for (int i = 0; i < size; i++) {
        /* 各要素を絶対値に変換して表示 */
        printf("%d ", abs(values[i]));
    }
    printf("\n");

    return 0;
}
実行結果
元の値: -3 -1 0 2 5 
絶対値: 3 1 0 2 5

このように、absはint型の単純な絶対値変換には非常に便利です。

特に、距離や差分など、符号は不要で大きさだけ扱いたい場面で有効です。

0やINT_MINをabsに渡すときの注意点

0をabsに渡したときの結果は直感通りで、常に0が返ってきます。

これは問題ありません。

注意すべきなのはINT_MINをabsに渡した場合です。

多くの環境でint型は32ビットで、範囲は次のようになっています。

  • INT_MIN = -2,147,483,648
  • INT_MAX = 2,147,483,647

絶対値は常に0以上なので、本来ならINT_MINの絶対値は+2,147,483,648になるはずですが、これはint型の範囲を1だけはみ出しています。

このため、abs(INT_MIN)の結果は未定義動作となります。

実際の環境では、例えば次のような結果になることがあります。

C言語
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

int main(void) {
    printf("INT_MIN = %d\n", INT_MIN);

    int x = INT_MIN;
    int y = abs(x);  /* ここは未定義動作の可能性 */

    printf("abs(INT_MIN) = %d\n", y);

    return 0;
}
実行結果
INT_MIN = -2147483648
abs(INT_MIN) = -2147483648  ← そのまま返る環境の一例

環境によってはそのままINT_MINが返ってきたり、オーバーフローして別の値になったりします。

いずれにしても、仕様として結果は保証されていない点が重要です。

従って、absを使うときは「INT_MINが入力されない」または「INT_MINは別扱いする」という前提が必要になります。

この点は、自作の絶対値関数を作るときにも大事な設計ポイントになります。

型別に見る絶対値の取り方

long型の絶対値を求めるlabs・llabs

int型よりも大きな整数を扱うときは、long型やlong long型を使うことが多いです。

これらの型にも専用の絶対値関数が用意されています。

  • long型: labs
  • long long型: llabs

宣言はいずれもstdlib.hにあります。

C言語
/* 標準ヘッダ内のイメージ */
long labs(long x);
long long llabs(long long x);

labsとllabsのサンプルコード

C言語
#include <stdio.h>
#include <stdlib.h>  /* labs, llabs */

int main(void) {
    long a = -1000000000L;            /* long型の負の値 */
    long long b = -9000000000000LL;   /* long long型の負の値 */

    long abs_a = labs(a);            /* longの絶対値 */
    long long abs_b = llabs(b);      /* long longの絶対値 */

    printf("a = %ld, labs(a) = %ld\n", a, abs_a);
    printf("b = %lld, llabs(b) = %lld\n", b, abs_b);

    return 0;
}
実行結果
a = -1000000000, labs(a) = 1000000000
b = -9000000000000, llabs(b) = 9000000000000

ここでも、long_min, long_long_minのような最小値に対しては、absと同様にオーバーフロー(未定義動作)の可能性があります。

整数の絶対値をとるときは、常に「最小値の扱い」を意識することが安全なコードのポイントです。

float・doubleの絶対値を求めるfabs・fabsf・fabsl

浮動小数点数の絶対値には、math.hに宣言されているfabsファミリを使います。

各関数の宣言イメージは次の通りです。

C言語
/* math.h 内のイメージ */
double fabs(double x);
float fabsf(float x);
long double fabsl(long double x);

fabsファミリを使うときの注意点として、コンパイル時に数学ライブラリをリンクする必要がある環境があることが挙げられます。

LinuxなどのUnix系環境では、コンパイル時に-lmオプションを付ける必要があることが多いです。

fabsのサンプルコード

C言語
#include <stdio.h>
#include <math.h>   /* fabs, fabsf, fabsl */

int main(void) {
    double dx = -3.14;
    float  fx = -1.23f;
    long double lx = -2.0L;

    /* それぞれ型に対応したfabsを使用 */
    double d_abs = fabs(dx);
    float  f_abs = fabsf(fx);
    long double l_abs = fabsl(lx);

    printf("dx = %f, fabs(dx) = %f\n", dx, d_abs);
    printf("fx = %f, fabsf(fx) = %f\n", fx, f_abs);
    printf("lx = %Lf, fabsl(lx) = %Lf\n", lx, l_abs);

    return 0;
}
実行結果
dx = -3.140000, fabs(dx) = 3.140000
fx = -1.230000, fabsf(fx) = 1.230000
lx = -2.000000, fabsl(lx) = 2.000000

浮動小数点数の場合、最小値の絶対値でもオーバーフローを起こさないよう定義されているため、整数のabsに比べれば扱いやすいです。

ただし、NaNや無限大(infinity)に対する挙動など、浮動小数特有の注意点は別途存在します。

マクロabsと関数fabsの混在に注意

実装によっては、absがマクロとしても定義されている場合があります。

たとえば#define abs(x) ...のような形です。

マクロは単なるテキスト置換なので、例えば次のような書き方をすると、思わぬ動作になる可能性があります。

C言語
#define abs(x) ((x) < 0 ? -(x) : (x))  /* ありがちなユーザ定義マクロ */

double d = -3.5;
double e = abs(d);  /* これはfabsではない */

この場合、abs(d)((d) < 0 ? -(d) : (d))に展開されるだけで、fabsは一切呼ばれません

そのため、意図せずマクロabsと標準関数absやfabsを混在させると、可読性の低下やバグの原因になります。

ベストプラクティスとしては、標準ライブラリのabsをそのまま使い、独自のマクロ名で上書きしないことが重要です。

もしどうしてもマクロで簡易的な絶対値を定義したい場合は、MY_ABSなど、標準と衝突しない名前にするのが安全です。

マイナス判定から自作関数まで

if文でマイナス判定して絶対値を求める方法

absやfabsを使わず、自前で絶対値を計算する方法も知っておくと便利です。

最も基本的なのはif文を使った符号判定です。

C言語
#include <stdio.h>

/* int型の値を絶対値に変換する関数 */
int my_abs_if(int x) {
    if (x < 0) {
        return -x;   /* 負のときだけ符号を反転 */
    } else {
        return x;    /* 0または正のときはそのまま */
    }
}

int main(void) {
    int v1 = -10;
    int v2 = 0;
    int v3 = 5;

    printf("my_abs_if(%d) = %d\n", v1, my_abs_if(v1));
    printf("my_abs_if(%d) = %d\n", v2, my_abs_if(v2));
    printf("my_abs_if(%d) = %d\n", v3, my_abs_if(v3));

    return 0;
}
実行結果
my_abs_if(-10) = 10
my_abs_if(0) = 0
my_abs_if(5) = 5

このような関数は単純で分かりやすく、デバッグ時に処理を追いやすいという利点があります。

ただし、ここでもINT_MINの扱いには注意が必要です。

xINT_MINの場合、-xはオーバーフローを起こします。

三項演算子で書く簡潔な絶対値関数

if文の代わりに三項演算子を使うと、絶対値関数をより簡潔に書くことができます。

C言語
#include <stdio.h>

/* 三項演算子で実装した絶対値関数 */
int my_abs_ternary(int x) {
    /* xが負なら -x、そうでなければ x を返す */
    return (x < 0) ? -x : x;
}

int main(void) {
    int v1 = -42;
    int v2 = 7;

    printf("my_abs_ternary(%d) = %d\n", v1, my_abs_ternary(v1));
    printf("my_abs_ternary(%d) = %d\n", v2, my_abs_ternary(v2));

    return 0;
}
実行結果
my_abs_ternary(-42) = 42
my_abs_ternary(7) = 7

この書き方は、短く記述できる反面、条件式に慣れていない人には少し読みにくくなる可能性があります。

チーム開発では、可読性とのバランスを考えて採用するかどうかを判断するとよいです。

いずれにしても、ここでもINT_MINに対してはオーバーフローの危険がある点は変わりません。

安全な実装を目指す場合は、次のセクションで説明する工夫が必要です。

オーバーフローを避ける絶対値の実装ポイント

整数の絶対値を自作するときの最大のポイントは「最小値(INT_MINなど)にどう対応するか」です。

素直に-xと書いてしまうと、その値の絶対値が表現範囲を超えてしまうことがあります。

1. INT_MINを特別扱いする(一例)

1つの考え方として、INT_MINが来た場合は別の値を返すという方法があります。

例えば、INT_MAXを返すか、処理不能として扱うかなどです。

C言語
#include <stdio.h>
#include <limits.h>

/* INT_MINを特別扱いする絶対値関数の一例 */
int my_abs_safe(int x) {
    if (x == INT_MIN) {
        /* ここでどう扱うかは設計次第 */
        /* 例: INT_MAXを返して近似とする */
        return INT_MAX;
    }
    return (x < 0) ? -x : x;
}

int main(void) {
    printf("my_abs_safe(-10) = %d\n", my_abs_safe(-10));
    printf("my_abs_safe(INT_MIN) = %d\n", my_abs_safe(INT_MIN));
    return 0;
}
実行結果
my_abs_safe(-10) = 10
my_abs_safe(INT_MIN) = 2147483647

この実装は数学的に厳密な絶対値にはなっていませんが、オーバーフローを避けるという意味では一定の安全性を確保できます。

どの値を返すかは仕様設計に依存します。

2. より広い型へ変換してから絶対値を取る

もう1つのアプローチとして、十分に広い型にキャストしてから絶対値を計算する方法があります。

例えば、intの値をlong longにキャストし、long longで絶対値を求めるといった方法です。

C言語
#include <stdio.h>
#include <limits.h>

/* long longを使ってintの絶対値を安全側で計算する */
long long my_abs_wide(int x) {
    long long y = (long long)x;  /* より広い型に変換 */

    if (y < 0) {
        y = -y;                  /* long longの範囲で符号反転 */
    }
    return y;
}

int main(void) {
    int a = -10;
    int b = INT_MIN;

    printf("my_abs_wide(%d) = %lld\n", a, my_abs_wide(a));
    printf("my_abs_wide(INT_MIN) = %lld\n", my_abs_wide(b));

    return 0;
}
実行結果
my_abs_wide(-10) = 10
my_abs_wide(INT_MIN) = 2147483648

このコードでは、戻り値の型をlong longにすることで、int最小値の絶対値も安全に表現しています。

その代わり、呼び出し側ではlong longとして値を扱う必要があります。

このように、表現範囲を広げることでオーバーフローを回避するのはよく使われるテクニックです。

3. 浮動小数点での絶対値はfabs系を使う

浮動小数点数については、基本的にfabsファミリを使えばよく、オーバーフローの心配は整数ほど大きくありません

自作の絶対値関数を作るよりも、標準ライブラリのfabs / fabsf / fabslを素直に使う方が安全で移植性も高いです。

まとめ

本記事では、C言語における絶対値の取り方を「abs」「labs・llabs」「fabsファミリ」から自作関数まで体系的に整理しました。

重要なポイントは、型ごとに対応する関数が異なることと、整数の最小値(INT_MINなど)ではオーバーフローが起こり得ることです。

標準関数を正しく使い分けつつ、必要に応じてif文や三項演算子、自作の安全な実装も組み合わせることで、より堅牢で読みやすいコードを書くことができます。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!