Why std :: abs (9484282305798401ull) = 9484282305798400?

I am currently writing a template helper method that can convert C numbers as a whole (including unsigned long long) to mpz_class numbers in the GMP library. Between them there is a call to std::abs .

However, it turns out that for C ++ 17 (g ++ 6.3.1),

 #include <iostream> #include <cmath> int main() { std::cout << (unsigned long long)std::abs(9484282305798401ull); } 

gives an incorrect conclusion 9484282305798400 .

As I understood from cmath , std::abs first passes the argument to double.

According to C ++ docs, double has 52 bits of mantissa, which means that the maximum integer value must be strictly less than 2^52 = 4503599627370496 before losing accuracy.

Is it possible to say that since 9484282305798401 exceeds this limit, std::abs ends the precision cancellation to give the wrong answer?

To clarify, I absolutely understand that it is absolutely not necessary to request the absolute value of unsigned integers; However, I would like the template function to work for C totals, rather than separately creating a specialization for each signed and unsigned type separately.

+7
c ++ floating-point precision c ++ 11 c ++ 17
source share
3 answers

Firstly, what you are doing does not really make sense out of context (getting the absolute value of an unsigned type). But I'm distracted.

The code you posted does not compile. At least not in the compiler I used (depending on which repl_it uses). Instead, he complains about ambiguous overload. Even if it is compiled, it will distinguish an unsigned long long from another type that cannot support its actual value (in this case, double ).

Change abs on llabs as follows:

 std::cout << (unsigned long long)std::llabs(9484282305798401ull); 

.. both make compilation and give exact result. See the documentation of the various abs functions for integer types here .

+5
source share

Your program is poorly formed. From [c.math.abs] /29.9.2.3 :

If abs() is called with an argument of type X , for which is_unsigned_v<X> is true , and if X cannot be converted to int by a single distribution, the program is poorly formed.

Compilers should warn you about this.

It also makes no sense to call std::abs on an unsigned type.

+8
source share

You can create your own abs overload if you want to manage unsigned types that differ from the standard library function:

 #include <cmath> #include <type_traits> namespace my { template <class S> auto abs (S x) -> typename std::enable_if<std::is_signed<S>::value, decltype(std::abs(x))>::type { return std::abs(x); } template <class U> auto abs (U x) -> typename std::enable_if<std::is_unsigned<U>::value, U>::type { return x; } } // namespace my 

Then

 std::cout << my::abs(9484282305798401ull) << '\n' // -> 9484282305798401 << my::abs(-3.14159) << '\n' // -> 3.14159 << my::abs(std::numeric_limits<char>::min()) << '\n' // -> 128 << my::abs(std::numeric_limits<int>::min()) << '\n' // -> -2147483648 

Please note that std::abs supports char (signed in my implementation), but due to 2 additional representations of int s it is not possible to get the absolute value of INT_MIN.

+3
source share

All Articles