abs関数というのは絶対値を求める関数です。C言語ではint型の引数を受け取りint型の値を返します。C++では、int版以外に、long版、float版、double版、long double版、std::complexクラステンプレート版が多重定義されています。絶対値を求める関数ですから、ゼロまたは正の値を返すのが数学的に正しい振る舞いです。

ところが、現実には必ずしもそのようにはなりません。まずは、浮動小数点数や複素数の引数として非数(NaN)が渡された場合がそうです。これについては、それほど大きな問題になることはないでしょう。問題は整数の場合です。

次に、abs関数にINT_MINを与えた場合を考えてみてください。負の値の内部表現に2の補数を使う処理系(実質的にすべての処理系と考えても、あながち間違いとはいえません)では、INT_MINの絶対値はINT_MAX + 1になることが大多数です。つまり、INT_MINの絶対値をint型で表現することができなくなるのです。

abs関数は、結果が表現できない場合の動作は未定義です。多くの場合、abs(INT_MIN)はINT_MIN(すなわち負の値)を返します。つまり、非ゼロではない値を返すのです。

他の可能性についても考えてみます。

負の値の内部表現は2の補数だけれども、符号ビットとすべての値ビットが1となるようなビットパターンがトラップ表現になる処理系も考えられます。この場合には、abs(INT_MIN)はINT_MAXになりますので問題ありません。ただし、引数にトラップ表現を与えた場合の動作は未定義になります。

負の値の内部表現に1の補数または符号ビットと絶対値を使う場合にも、abs(INT_MIN)はINT_MAXになります。ただし、この場合もトラップ表現を引数として与えた場合の動作は未定義になります。

現実には、負の値の内部表現に2の補数以外を使っている処理系に遭遇することはまずありません。C++20以降では負の値の内部表現が2の補数になることが規格で規定されています。また、トラップ表現を使っている処理系にも遭遇することはないと思います。ですので、NaNやINT_MINをabs関数に渡したときのことだけ考慮すれば十分だと考えます。