Computing absolute values in C/C++
C includes various functions for computing the absolute value of a signed number. C++98 implementations add the C functions to namespace std
, and it adds abs()
overloads to namespace std
so std::abs
works on everything. For a long time Mozilla used NS_ABS
to compute absolute value, but recently we switched to std::abs
. This works on many systems, but it has a few issues.
Issues with std::abs
std::abs
is split across two headers
With some compilers, the integral overloads are in <cstdlib>
and the floating point overloads are in <cmath>
. This led to confusion when std::abs
compiled on one type but not on another, in the same file. (Or worse, when it worked with just one #include
because of that developer’s compiler.) The solution was to include both headers even if only one was needed. This is pretty obscure.
std::abs(int64_t)
doesn’t work everywhere
On many systems <stdint.h>
has typedef long long int64_t;
. But long long
was only added in C99 and C++11, and some compilers don’t have long long std::abs(long long)
, so int64_t i = 0; std::abs(i);
won’t compile. We “solved” this with compiler-specific #ifdef
s around custom std::abs
specializations in a somewhat-central header. (That’s three headers to include!) C++ says this has undefined behavior, and indeed it’ll break as we update compilers.
std::abs(int32_t(INT32_MIN))
doesn’t work
The integral abs
overloads don’t work on the most-negative value of each signed integer type. On twos-complement machines (nearly everything), the absolute value of the smallest integer of a signed type won’t fit in that type. (For example, INT8_MIN
is -128
, INT8_MAX
is +127
, and +128
won’t fit in int8_t
.) The integral abs
functions take and return signed types. If the smallest integer flows through, behavior is undefined: as absolute-value is usually implemented, the value is returned unchanged. This has caused Mozilla bugs.
Mozilla code should use mozilla::Abs
, not std::abs
Unfortunately the only solution is to implement our own absolute-value function. mozilla::Abs
in "mozilla/MathAlgorithms.h"
is overloaded for all signed integral types and the floating point types, and the integral overloads return the unsigned type. Thus you should use mozilla::Abs
to compute absolute values. Be careful about signedness: don’t assign directly into a signed type! That loses mozilla::Abs
‘s ability to accept all inputs and will cause bugs. Ideally this would be a compiler warning, but we don’t use -Wconversion
or Microsoft equivalents and so can’t do better.
As a brief footnote, people wondering why all the standard integral
abs
functions return the signed type may find this Stack Overflow question informative.Comment by Jeff — 30.04.13 @ 11:17
If only you could write
(I hope this works, given that there’s no preview…)
(Side note: why do you need const T t when T is a primitive type?)
Comment by Neil Rashbrook — 01.05.13 @ 04:36
It’s possible to write a
template<typename T> MakeUnsigned
containing a membertypedef
that does that. C++11<type_traits>
hasstd::make_unsigned
that does exactly this, and nothing in it requires C++11. I imagine we’ll pick it up at some point, too, inmozilla/TypeTraits.h
.There’s no particular reason to use
const T t
except (perhaps) extra clarity about the parameter not being modified.Comment by Jeff — 01.05.13 @ 10:29