このページでは、C言語
で値の “絶対値” を求める方法について解説していきます。
Contents
関数(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
をインクルードしておく必要があります。
特に 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
の絶対値を取得することができます。
#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
の絶対値を求める例になります。
#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
型で扱えない大きな値)の絶対値を求める場合は、labs
や llabs
を使用する必要があります。
double
型の変数の絶対値を求める(fabs
関数を使用)
また、double
型の変数 x
に格納された値の絶対値を求めたいのであれば、下記のように fabs
関数を実行することで、x
の絶対値を取得することができます。
#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
関数を使用するのでオーケーです。
#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
に格納される)。
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
が、成立しないとき x
が abs_x
に格納される)。
abs_x = x < 0 ? -x : x;
関数のマクロで絶対値を求める
さらに、上記を関数形式のマクロに仕立ててやれば、数値を扱う基本的な型であれば、どんな型でも絶対値を求めることができる ABS
マクロの出来上がりです。
#define ABS(x) ((x) < 0 ? -(x) : (x))
この ABS
マクロは、関数(abs 関数など)を使って絶対値を求める で紹介した関数同様に、引数に絶対値を求めたい値を指定して実行することで、返却値としてその値の絶対値を得ることができます。
ただし、関数(abs 関数など)を使って絶対値を求める で紹介した関数とは異なり、ABS
マクロの場合は引数の型が数値を扱う型であればどの型であっても実行可能です。
例えば下記では int
型の変数 x
と double
型の変数 y
の絶対値を求めるために 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の補数” が採用されていることが原因です。
負の値の表現方法が2の補数でない環境であれば、最小値の絶対値も正常に求められるかもしれません
が、ほとんどの環境では、負の値の表現には2の補数表現が用いられていますので、基本的には上記の注意点に気をつける必要があると考えた方が良いです
関数を使わずに絶対値を求める でも解説したように、負の値の絶対値は、その値の符号を反転させることで求めることができます。さらに、負の値の表現方法が2の補数である場合、その符号の反転は、その値の2の補数を算出することで実現されます。
例えば前述のように、char
型の変数 x
に -128
が格納されており(-128
は char
型で扱える値の最小値)、この x
の絶対値を求めることを考えてみましょう。
x
を2進数で表すと下記のようになります。8桁なのは、char
型のサイズのビット数が8だからです。
1000000
さらに、この x
の絶対値を求めることを考えると、符号の反転を行えば良い、すなわち2の補数を算出すれば良いことになります。2の補数は、全ての桁の 0
と 1
の反転を行い、さらに、その反転結果に対して +1
することで求めることができます。
つまり、x
の2の補数を求める際には、まず下記のように 0
と 1
の反転を行い、
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言語
には絶対値を求めるための標準ライブラリ関数が用意されていますので、これらの関数を使用することで簡単に絶対値を求めることが可能です。
ただし、これらの関数を使う場合は、関数の引数の型(仮引数の型)と関数呼び出し時に引数に指定する変数の型(実引数の型)とで整合性が取れているかをしっかり意識する必要があります。
また、関数を使わずに絶対値を求める でも解説したように、関数を使わなくても絶対値は簡単に求めることができます。ですので、ヘッダーのインクルードなど無しにサクッと絶対値を求めたいような場合は、ここで説明した方法で絶対値を求めてやれば良いと思います。
いずれの方法においても、絶対値を求める際の注意点 で説明したように、特に符号ありの整数型の変数の絶対値を求める際には、最小値の絶対値が正常に求められないことに注意してください。
こういった計算結果がおかしくなるのは使用する型に原因があることが多いです。型で扱える値をしっかり意識しながらプログラミングするようにしましょう!
オススメの参考書(PR)
C言語学習中だけど分からないことが多くて挫折しそう...という方には、下記の「スッキリわかるC言語入門」がオススメです!
まず学習を進める上で、参考書は2冊持っておくことをオススメします。この理由は下記の2つです。
- 参考書によって、解説の仕方は異なる
- 読み手によって、理解しやすい解説の仕方は異なる
ある人の説明聞いても理解できなかったけど、他の人からちょっと違った観点での説明を聞いて「あー、そういうことね!」って簡単に理解できた経験をお持ちの方も多いのではないでしょうか?
それと同じで、1冊の参考書を読んで理解できない事も、他の参考書とは異なる内容の解説を読むことで理解できる可能性があります。
なので、参考書は2冊持っておいた方が学習時に挫折しにくいというのが私の考えです。
特に上記の「スッキリわかるC言語入門」は、他の参考書とは違った切り口での解説が豊富で、他の参考書で理解できなかった内容に対して違った観点での解説を読むことができ、オススメです。題名の通り「なぜそうなるのか?」がスッキリ理解できるような解説内容にもなっており、C言語入門書としてもかなり分かりやすい参考書だと思います。
もちろんネット等でも色んな観点からの解説を読むことが出来ますので、分からない点は別の人・別の参考書の解説を読んで解決していきましょう!もちろん私のサイトも参考にしていただけると嬉しいです!
入門用のオススメ参考書は下記ページでも紹介していますので、こちらも是非参考にしていただければと思います。
https://daeudaeu.com/c_reference_book/