【C言語】絶対値を求める方法(abs関数の利用・関数使わない・マクロなど)

C言語で絶対値を求める方法の解説ページアイキャッチ

このページでは、C言語 で値の “絶対値” を求める方法について解説していきます。

関数(abs 関数など)を使って絶対値を求める

C言語 には、標準ライブラリ関数として絶対値を求める関数が用意されています。

ですので、この関数を実行するだけで、絶対値を求めることが可能です。

また、この絶対値を求める関数は、絶対値を求めたい値や変数の “型” ごとに関数が用意されています。

絶対値を求める関数一覧

その関数の一覧が下記になります。

整数型の値の絶対値を求める
#include <stdlib.h>

int abs(int);
long labs(long);
long long llabs(long long); /* 存在しない場合もあるかも */
浮動小数点数型の値の絶対値を求める
#include <math.h>

double fabs(double);
float fabsf(float);
long double fabsl(long double); /* 存在しない場合もあるかも */

上記の全ての関数において引数と返却値の意味は同じです。

  • 引数:絶対値を求めたい値(もしくは絶対値を求めたい値を格納した変数)
  • 返却値:引数の値の絶対値

あとは、引数に指定する値・その値を格納した変数の型に応じて実行する関数を使い分ければ良いだけです。

ただし、整数の絶対値を求める関数と浮動小数点数の絶対値を求める関数とでプロトタイプ宣言されているヘッダーファイルが異なるので注意してください。

abs 等の整数の絶対値を求める関数を使用する場合は stdlib.h をインクルードしておく必要があります。

その一方で、fabs 等の浮動小数点数の絶対値を求める関数を使用する場合は math.h をインクルードしておく必要があります。

MEMO

特に math.h をインクルードする場合、環境によってはコンパイル時に libm をリンクする必要がある可能性があるので注意してください。

下記のようなエラーメッセージが出た場合は、libm をリンクする必要がありますので、コンパイル時のコマンドで -lm を指定するようにしてください。

Undefined symbols for architecture x86_64:
  "_abs", referenced from:
      _main in abs-c81919.o

スポンサーリンク

関数の使用例

では、先ほど紹介した関数の使用例を示していきたいと思います。

int 型の変数の絶対値を求める(abs 関数を使用)

例えば int 型の変数 x に格納された値の絶対値を求めたいのであれば、下記のように abs 関数を実行することで、x の絶対値を取得することができます。

abs関数の使用例
#include <stdio.h>
#include <stdlib.h> /* abs */

int main(void) {
    int x;
    int abs_x;

    /* xに絶対値を求めたい値を格納 */
    x = -1234; /* 負の値を格納 */

    /* absにより絶対値を求める */
    abs_x = abs(x);

    /* 1234が表示される */
    printf("%d\n", abs_x);

    x = 1234; /* 正の値を格納 */

    abs_x = abs(x);

    /* 1234が表示される */
    printf("%d\n", abs_x);

    return 0;
}

値を直接指定して絶対値を求める(abs 関数を使用)

また、関数の引数に値そのものを直接指定して絶対値を求めることも可能です。下記は abs 関数の引数に直接 -1234 を指定して -1234 の絶対値を求める例になります。

abs関数の使用例(値を直接指定)
#include <stdio.h>
#include <stdlib.h> /* abs */

int main(void) {
    int abs_x;

    abs_x = abs(-1234);

    /* 1234が表示される */
    printf("%d\n", abs_x);

    return 0;
}

上記の場合は引数に整数を指定するので abs 関数を使用していますが、浮動小数点数を指定するのであれば fabs 等を指定する必要があります。また整数であっても極端に大きな値(int 型で扱えない大きな値)の絶対値を求める場合は、labsllabs を使用する必要があります。

double 型の変数の絶対値を求める(fabs 関数を使用)

また、double 型の変数 x に格納された値の絶対値を求めたいのであれば、下記のように fabs 関数を実行することで、x の絶対値を取得することができます。

fabs関数の使用例
#include <stdio.h>
#include <math.h> /* fabs */

int main(void) {
    double x;
    double abs_x;

    /* xに絶対値を求めたい値を格納 */
    x = -1234.567; /* 負の値を格納 */

    /* absにより絶対値を求める */
    abs_x = fabs(x);

    /* 1234.567000が表示される */
    printf("%f\n", abs_x);

    x = 1234.567; /* 正の値を格納 */

    abs_x = fabs(x);

    /* 1234.567000が表示される */
    printf("%f\n", abs_x);

    return 0;
}

char 型や short 型の変数の絶対値を求める(abs 関数を使用)

char 型と short 型の変数に対して絶対値を求める関数は存在しませんが、char 型と short 型の変数の絶対値を求めたい場合は abs 関数を使用するのでオーケーです。

charやshortの変数の絶対値算出の例
#include <stdio.h>
#include <stdlib.h> /* abs */

int main(void) {
    char x;
    char abs_x;

    short y;
    short abs_y;

    /* char型の変数の絶対値を求める例 */
    x = -123;

    abs_x = abs(x);

    /* 123が表示される */
    printf("%d\n", abs_x);

    /* short型の変数の絶対値を求める例 */
    y = -1234;

    abs_y = abs(y);

    /* 1234が表示される */
    printf("%d\n", abs_y);

    return 0;
}

int 型の変数の絶対値を求める abs 関数で char 型と short 型の変数の絶対値を求められる理由は、int 型のサイズが char 型や short 型のサイズよりも大きいからです(表現できる値の範囲が広い)。

abs 関数のダメな使用例

その一方で、int 型よりも大きなサイズの型(例えば long long 型など)の変数を abs 関数により絶対値を求めようとすると、結果がおかしくなるので注意してください。

例えば私の環境で下記を実行すると、

変換に失敗する例
#include <stdio.h>
#include <stdlib.h> /* abs */

int main(void) {
    long long x;
    long long abs_x;

    /* long long型の変数の絶対値を求める例 */
    x = -112233445566;

    abs_x = abs(x);

    /* 112233445566と表示されて欲しいが... */
    printf("%lld\n", abs_x);

    return 0;
}

表示される値は下記のようになり、求めた絶対値がおかしくなりました。

564295870

私の環境では int 型のサイズは4バイト、long long 型のサイズは8バイトとして扱われますので、これを前提に上記のような現象が起きる理由を説明しておきます。

まず abs 関数の引数は int 型ですので、long long 型の変数を引数に指定した場合、long long 型から int 型に強制的に型変換された値が関数に渡されることになります。

long long 型のサイズが8バイトであるのに対して int 型のサイズは4バイトなので4バイトの情報がこの型変換時に失われることになります。この時失われるのは、long long 型の変数 x の上位4バイトになります。つまり、変数 x の下位4バイトのみが abs 関数に渡されることになります。

-112233445566 を下位4バイトのみで考えた場合の値は 564295870 ですので、abs 関数では 564295870 の絶対値を求めることになります。当然結果は 564295870 になりますので、abs_x に格納される値も 564295870 となります。

このように、関数の引数の型よりも大きなサイズの型の変数を引数に指定してしまうと、求まる絶対値がおかしくなることがあります。

ですので、char 型と short 型の変数に関しては abs 関数を使用し、その他の型の変数に対しては、その型に対応した関数を使用するようにしましょう!

  • int 型:abs 関数
  • long 型:labs 関数
  • long long 型:llabs 関数
  • double 型:fabs 関数
  • float 型:fabsf 関数
  • long double 型:fabsl 関数

関数の引数の型(仮引数の型)と関数呼び出し時に引数に指定する変数の型(実引数の型)とで話が合わない場合は、コンパイル時に下記のような警告が表示されるはずです(下記は私の環境で表示された警告です。環境によって警告文は異なると思いますので注意してください)。

warning: absolute value function 'abs' given an argument of type 'long long' but has parameter of type 'int' which may cause truncation of value [-Wabsolute-value]

ですので、コンパイル時の警告に注意していれば、上記のように絶対値がおかしくなるようなことは防ぐことができるはずです。

ここまで解説してきたように、関数を利用して絶対値を求める場合、変数の型をしっかり意識して関数の呼び分けを行う必要があります。

もし関数の呼び分けをしたくないのであれば、以降で紹介する関数のマクロを利用する方法がオススメです。

関数を使わずに絶対値を求める

続いて関数を使わずに絶対値を求める方法を解説していきます。

-1 を掛けて絶対値を求める

まず絶対値の求め方を整理していきましょう。

この絶対値の求め方は、正の値の場合と負の値の場合とで異なります。

  • 正の値の場合:その値そのもの
  • 負の値の場合:その値の符号を反転(マイナス符号をプラス符号に反転)したもの

この符号の反転は、-1 を掛けることで実現することができます。

また、負の値かどうかは値が 0 よりも小さいかどうかで判断することができますので、変数 x の値の絶対値の算出は下記の処理により行うことができます(x の絶対値が abs_x に格納される)。

-1を掛けて絶対値を求める
if (x < 0) {
    abs_x = x * -1;
} else {
    abs_x = x;
}

関数(abs 関数など)を使って絶対値を求める で紹介した方法では、関数の引数で指定可能な変数の型が決められてしまっていたため、変数 x の型に応じて関数を呼び分ける必要がありました。

その一方で、この方法の場合、数値を扱う基本的な型の変数であれば、全て上記の方法で絶対値を求めることができます。

スポンサーリンク

- 符号を付加して絶対値を求める

また、上記では -1 を掛けていますが、変数名の前に - 符号をつけるだけでも同様の結果を得ることができます(どちらも行っているのは符号の反転)。

-符号をつけて絶対値を求める
if (x < 0) {
    abs_x = -x;
} else {
    abs_x = x;
}

三項演算子を利用して絶対値を求める

これくらいの条件分岐であれば三項演算子を使って下記のように簡潔に記述することもできます(x < 0 が成立するとき -x が、成立しないとき xabs_x に格納される)。

三項演算子で絶対値を求める
abs_x = x < 0 ? -x : x;

関数のマクロで絶対値を求める

さらに、上記を関数形式のマクロに仕立ててやれば、数値を扱う基本的な型であれば、どんな型でも絶対値を求めることができる ABS マクロの出来上がりです。

絶対値を求めるマクロ関数
#define ABS(x) ((x) < 0 ? -(x) : (x))

この ABS マクロは、関数(abs 関数など)を使って絶対値を求める で紹介した関数同様に、引数に絶対値を求めたい値を指定して実行することで、返却値としてその値の絶対値を得ることができます。

ただし、関数(abs 関数など)を使って絶対値を求める で紹介した関数とは異なり、ABS マクロの場合は引数の型が数値を扱う型であればどの型であっても実行可能です。

例えば下記では int 型の変数 xdouble 型の変数 y の絶対値を求めるために ABS を実行していますが、どちらも正常に絶対値を求めることに成功しています。

ABSの使用例
#include <stdio.h>

/* 絶対値を求める関数のマクロを定義 */
#define ABS(x) ((x) < 0 ? -(x) : (x))

int main(void) {
    int x, abs_x;
    double y, abs_y;

    x = -1234;

    abs_x = ABS(x);

    /* 1234が表示される */
    printf("%d\n", abs_x);

    y = -1234.567;

    abs_y = ABS(y);

    /* 1234.567000が表示される */
    printf("%f\n", abs_y);

    return 0;
}

これができるのは、ソースコード上の ABS(x) と ABS(y) の部分が、コンパイルを行う前にプリプロセッサによって下記のように置き換えが行われるためです(include の部分のプリプロセッサによる置き換え部分は省略しています)。

プリプロセス後のコード
int main(void) {
    int x;
    int abs_x;

    double y;
    double abs_y;

    x = -1234;

    abs_x = ((x) < 0 ? -(x) : (x));


    printf("%d\n", abs_x);

    y = -1234.567;

    abs_y = ((y) < 0 ? -(y) : (y));


    printf("%f\n", abs_y);

    return 0;
}

つまり、プリプロセッサにより関数呼び出しではなく 三項演算子を利用して絶対値を求める で紹介した式に置き換えられるため、関数の引数の型の考慮が不要になります。

この辺りのプリプロセッサやマクロについては下記ページで解説していますので、詳しく知りたい方は下記ページをご参照しただければと思います。

プリプロセッサ解説ページアイキャッチ【C言語】プリプロセッサについて解説!#includeや#defineの意味が理解できる!

いろんな型の変数の絶対値を求めたい&ソースコードのいろんな位置から絶対値を求めたいような場合は、上記の関数のマクロ ABS を利用するのが便利だと思います。

スポンサーリンク

絶対値を求める際の注意点

最後に絶対値を求める際の注意点について説明しておきます。

符号ありの整数型の変数に対して絶対値を求める場合、「その型で扱える値の最小値の絶対値は正常に求められない」ので注意してください。

例えば、char 型で扱える最小値は -128 ですが、この絶対値は 128 として算出されるのではなく、-128 として算出されてしまいます(そもそも char 型で扱える値の最大値は 127 なので 128 を扱うことができない)。

これは 関数(abs 関数など)を使って絶対値を求める で紹介した方法においても、関数を使わずに絶対値を求める で紹介した方法においても同様に発生する注意点になります。

このような現象が発生するのは、負の値の表現方法に “2の補数” が採用されていることが原因です。

MEMO

負の値の表現方法が2の補数でない環境であれば、最小値の絶対値も正常に求められるかもしれません

が、ほとんどの環境では、負の値の表現には2の補数表現が用いられていますので、基本的には上記の注意点に気をつける必要があると考えた方が良いです

関数を使わずに絶対値を求める でも解説したように、負の値の絶対値は、その値の符号を反転させることで求めることができます。さらに、負の値の表現方法が2の補数である場合、その符号の反転は、その値の2の補数を算出することで実現されます。

例えば前述のように、char 型の変数 x-128 が格納されており(-128char 型で扱える値の最小値)、この x の絶対値を求めることを考えてみましょう。

x を2進数で表すと下記のようになります。8桁なのは、char 型のサイズのビット数が8だからです。

1000000

さらに、この x の絶対値を求めることを考えると、符号の反転を行えば良い、すなわち2の補数を算出すれば良いことになります。2の補数は、全ての桁の 01 の反転を行い、さらに、その反転結果に対して +1 することで求めることができます。

つまり、x の2の補数を求める際には、まず下記のように 01 の反転を行い、

01111111

さらに、上記に対して +1 することになります。この時、最下位の桁に対して +1 されることになりますが、上位の桁に桁上がりが発生していって結局は下記のようになります。

1000000

元に戻っちゃいましたね!つまり x に格納されていた値 -128 の絶対値は -128 として算出されることになります。

上記は char 型の例になりますが、他の型でも同様で、その型で扱える値の最小値に対して2の補数を算出すると、その最小値に戻ってしまう現象が発生します(最小値を2進数で考えた時、必ず最上位の桁のみが 1 であり、他の桁が 0 になる)。

もし、符号ありの整数型の最小値の絶対値を求める可能性がある場合は、その型よりもサイズの大きな型を使用するようにした方が良いです。

例えばプログラムの中で char 型の最小値である -128 の絶対値を求める可能性があるのであれば、値は char 型ではなく、char 型よりも大きなサイズの型、例えば short 型や int 型の変数で扱うようにした方が良いです。

そうすれば、正常に絶対値を求めることができます(-128 の絶対値であれば 128 として求めることができる)。

ちなみに、浮動小数点数の場合(double 型や float 型の場合)、負の値の表現方法が2の補数ではないので上記のような問題は発生しないはずです(指数部のみを2の補数で表現したりはしますが)。

まとめ

このページでは、C言語 で絶対値を求める方法について解説しました!

関数(abs 関数など)を使って絶対値を求める で解説したように、C言語 には絶対値を求めるための標準ライブラリ関数が用意されていますので、これらの関数を使用することで簡単に絶対値を求めることが可能です。

ただし、これらの関数を使う場合は、関数の引数の型(仮引数の型)と関数呼び出し時に引数に指定する変数の型(実引数の型)とで整合性が取れているかをしっかり意識する必要があります。

また、関数を使わずに絶対値を求める でも解説したように、関数を使わなくても絶対値は簡単に求めることができます。ですので、ヘッダーのインクルードなど無しにサクッと絶対値を求めたいような場合は、ここで説明した方法で絶対値を求めてやれば良いと思います。

いずれの方法においても、絶対値を求める際の注意点 で説明したように、特に符号ありの整数型の変数の絶対値を求める際には、最小値の絶対値が正常に求められないことに注意してください。

こういった計算結果がおかしくなるのは使用する型に原因があることが多いです。型で扱える値をしっかり意識しながらプログラミングするようにしましょう!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です