Cannot overload std::abs() for GCC 128 bit integer - c++

I'm getting compiler errors when I call abs() with GCC's 128 bit type. My line looks like:
__extension__ using int128_t = __int128;
int128_t mantissa_;
// Other code
using namespace std;
int128_t temp = abs(mantissa_);
The error I'm getting locally is:
error: call of overloaded ‘abs(const int128_t&)’ is ambiguous
int128_t temp = abs(mantissa_);
/usr/include/stdlib.h:840:12: note: candidate: ‘int abs(int)’
840 | extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur;
/usr/include/c++/11/bits/std_abs.h:56:3: note: candidate: ‘long int std::abs(long int)’
56 | abs(long __i) { return __builtin_labs(__i); }
| ^~~
/usr/include/c++/11/bits/std_abs.h:61:3: note: candidate: ‘long long int std::abs(long long int)’
61 | abs(long long __x) { return __builtin_llabs (__x); }
| ^~~
/usr/include/c++/11/bits/std_abs.h:71:3: note: candidate: ‘constexpr double std::abs(double)’
71 | abs(double __x)
| ^~~
/usr/include/c++/11/bits/std_abs.h:75:3: note: candidate: ‘constexpr float std::abs(float)’
75 | abs(float __x)
| ^~~
/usr/include/c++/11/bits/std_abs.h:79:3: note: candidate: ‘constexpr long double std::abs(long double)’
79 | abs(long double __x)
so it's not considering my overload (below) as a candidate?
namespace std
{
__extension__ using int128_t = __int128;
int128_t abs(int128_t x)
{
return x < 0 ? x * -1 : x; // Not ideal but no builtin for 128
}
}
Is the overload correct?
However, when I mock an example in Godbolt, even without overloading abs(), it compiles fine:
https://godbolt.org/z/P8T1fGxcK
#include <iostream>
__extension__ using int128_t = __int128;
struct Decimal
{
Decimal(int128_t q) : mantissa_(q)
{
volatile int128_t x = abs(mantissa_); // How is this compiling?
std::cout << "ctor" << std::endl;
}
int128_t mantissa_;
};
int main()
{
Decimal dec(-6);
}
Are Godbolt using a library like Abseil, they're providing a function and that's why it is compiling?

1. How to get std::abs working for __int128
The following only applies for gcc version >= 7.
For __int128 gcc provides an overload of std::abs with the following code snippet:
libstdc++-v3/include/bits/std_abs.h
#if defined(__GLIBCXX_TYPE_INT_N_0)
__extension__ inline _GLIBCXX_CONSTEXPR __GLIBCXX_TYPE_INT_N_0
abs(__GLIBCXX_TYPE_INT_N_0 __x) { return __x >= 0 ? __x : -__x; }
#endif
(or one of the following 3 similar functions using __GLIBCXX_TYPE_INT_N_1, __GLIBCXX_TYPE_INT_N_2, etc...)
To get those macros defined you need to compile with gnu extensions enabled (i.e. not strict c++)
For this you need to build with -std=gnu++20 instead of -std=c++20 (or -std=gnu++17, -std=gnu++14, etc... depending on the c++ version you want to target)
(if no -std= option is given at all then gcc will default to std=gnu++xx (c++ version depending on compiler version))
When compiling with gnu extensions gcc will define those macros automatically for __int128 (assuming gcc supports 128-bit ints for your target platform):
godbolt
#define __GLIBCXX_BITSIZE_INT_N_0 128
#define __GLIBCXX_TYPE_INT_N_0 __int128
Unfortunately the c abs function-family (and the gcc builtin for it - __builtin_abs) do not support __int128 at all, and calling them would result in truncation of the result value.
Example: (compiled with -std=gnu++20 -Wconversion):
godbolt
#include <cmath>
__extension__ using int128_t = __int128;
int128_t mantissa_;
int main() {
{
using namespace std;
// OK - calls std::abs(__int128)
int128_t foo = abs(mantissa_);
}
// OK - calls std::abs(__int128)
int128_t bar = std::abs(mantissa_);
// WARNING: calls abs(int) --> truncation
int128_t baz = abs(mantissa_);
// WARNING: calls abs(int) --> truncation
int128_t foobar = __builtin_abs(mantissa_);
}
2. Why the godbolt compiles
The godbolt you provided compiles due to it calling the c function int abs(int)
(the code does not include using namespace std;, so std::abs is not visible)
The c abs-family of functions have different names for the possible types, so a call to abs() with __int128 as argument will not be ambigous (but will result in truncation to int):
int abs ( int n );
long labs ( long n );
long long llabs( long long n );
/* floating-point versions would be fabs, ... */
The c++-variant of abs - std::abs - is implemented with overloads instead, so a call to std::abs with __int128 will be ambigous, assuming there is no overload for __int128 defined.
(__int128 -> long long, __int128 -> long, __int128 -> int, etc... would be Integral conversions; __int128 -> double, __int128 -> float, etc.. would be Floating-integral conversions. Both Integral conversions and Floating-integral conversions and have the same rank (Conversion), so none of those overloads would be a better match than any other -> ambigous)
namespace std {
int abs( int n );
long abs( long n );
long long abs( long long n );
/* more overloads of abs for floating-point types */
}
3. Manually adding the std::abs function
Note that adding declarations to std is generally undefined behaviour (with a few exceptions), so i would not recommend this.
Here's a comparison of the gcc version vs your version:
// version provided by gcc (when compiling with gnu extensions)
// (macros substituted for better readability)
namespace std {
__extension__ inline constexpr __int128 abs(__int128 __x) {
return __x >= 0 ? __x : -__x;
}
}
// your version
namespace std {
__extension__ using int128_t = __int128;
int128_t abs(int128_t x)
{
return x < 0 ? x * -1 : x; // Not ideal but no builtin for 128
}
}
They're functionally identical, the ternary condition is just inverted and you're multiplying by negative one whereas gcc uses the unary minus operator.
godbolt example

Related

To solve ambiguity of overloaded function without either adding typecasting or remove the overloaded functions

I made a class Dummy, which is just a wrapper of primitive type double. The purpose of the class is to test potential problems before I introduce another class that substitutes the double class.
Dummy.h:
class Dummy{
private:
double content;
public:
Dummy(double _content) :content{ _content } {};
operator long int() {return (long int)content;};
}
test.cpp:
#include <math.h>
int main(){
Dummy a = Dummy(1.1);
double aa = fabs(a);
}
It reports:
<source>:17:27: error: call of overloaded 'fabs(Dummy&)' is ambiguous
17 | std::cout << std::fabs(foo);
| ~~~~~~~~~^~~~~ In file included from /usr/include/features.h:461,
from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/x86_64-linux-gnu/bits/os_defines.h:39,
from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/x86_64-linux-gnu/bits/c++config.h:586,
from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/cmath:41,
from <source>:1: /usr/include/x86_64-linux-gnu/bits/mathcalls.h:162:1: note: candidate:
'double fabs(double)'
162 | __MATHCALLX (fabs,, (_Mdouble_ __x),
(__const__));
| ^~~~~~~~~~~ In file included from <source>:1: /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/cmath:241:3:
note: candidate: 'constexpr float std::fabs(float)'
241 |
fabs(float __x)
| ^~~~ /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/cmath:245:3:
note: candidate: 'constexpr long double std::fabs(long double)'
245 | fabs(long double __x)
If I add a type conversion operator:
operator double(){
return content;
}
It somehow only works under Visual Studio, not g++ 9.3.0, which is my target compiler.
During the compiling with g++ 9.3.0, the following errors are reported:
I have read the information here, unfortunately I can't remove the overloaded functions in this case. Neither can I add typecasting, because it doesn't make sense for my purpose.
I also check here, but I am not so familiar with templates. Hence my question is:
Can I eliminate the ambiguity of overloaded function within the class, without adding typecasting, or removing the overloaded functions?
The Dummy class you have shown is only convertible to long int, but fabs() does not accept long int, it accepts either float, double, or long double. Since long int is implicitly convertible to float and double, the call was ambiguous until you added the double conversion operator.
If you don't want to do that, then you have to be explicit about which overload of fabs() you want to call. Either by:
casting the result of the Dummy conversion:
double aa = fabs(static_cast<double>(a));
assigning/casting fabs() to a function pointer of the desired signature:
double (*fabs_ptr)(double) = fabs;
fabs_ptr(a);
using fabs_ptr = double (*)(double);
static_cast<fabs_ptr>(fabs)(a);
Online Demo

error: narrowing conversion of ‘18446744069414584320ull’ from ‘long long unsigned int’ to ‘int’ inside { } [-Wnarrowing]

What is wrong with this piece of code? I'm using GCC 6.3.
https://ideone.com/ektGEp
a.cpp: In function ‘int main()’:`a.cpp:26:24: error: narrowing conversion of ‘18446744069414584320ull’ from ‘long long unsigned int’ to ‘int’ inside { } [-Wnarrowing]
#include <iostream>
using namespace std;
#include <smmintrin.h>
#include <emmintrin.h>
#include <tmmintrin.h>
typedef union {
__m64 mm64[2];
__m128 mm128i;
} sse2_t;
#define const_epi32( i3,i2,i1,i0 ) \
{ static_cast<unsigned long long> (static_cast<unsigned long long>(i1) << 32), \
static_cast<unsigned long long> (static_cast<unsigned long long>(i3) << 32) }
int main() {
sse2_t arr_val[3] = { const_epi32(0,-1,0,-1),
const_epi32(0, 0,-1, -1),
const_epi32(0, 0,0, 1024)
}; //error!
sse2_t val = const_epi32(0,-1,0,-1); // ok!
// your code goes here
return 0;
}
The types __m64 and __m128 are very special, representing vector registers, which means that you cannot assign values to them in the usual way (like you seem to be trying to do in your code). You need to use special load functions to put data into registers, and special store functions to get data back from there.
Here is an example of how you could load data (four floats) into a __m128 variable:
#include <cstring>
#include <smmintrin.h>
int main() {
float f[4];
memset(f, 0, 16);
__m128 a = _mm_load_ps(f);
return 0;
}
This page has lots of info about all the different types and intrinsic functions available: https://software.intel.com/sites/landingpage/IntrinsicsGuide/
#define const_epi32( i3,i2,i1,i0 ) \
{ _m_from_int64( (static_cast<unsigned long long>(i1) << 32), \
_m_from_int64( (static_cast<unsigned long long>(i3) << 32) }
The part const_epi32(0, 0,-1, -1) is causing the warning since you are assigning '0xffffFFFFffffFFFF' to a 32 bit 'int' which is narrowing.
I assume the other cases do not cause a warning since the assigned value is a constant 0 which does not suffer from narrowing.
The other question is, why is __m64 an int. I assume that __m64 cannot be directly assigned to (being an MM register) and that the narrowing check (at least for constant assignment) is bogus in the compiler.

How to use the C++ max function with boost 128 bit integer

If I have the following code:
#include <boost/multiprecision/cpp_int.hpp>
using namespace boost::multiprecision
int main()
{
int128_t a = Func_a()
int128_t b = Func_b()
std::cout << std::max(a, b) << std::endl;
return 0;
}
And if I compile using g++ on Ubuntu, I get the following error:
error: cannot convert ‘const boost::multiprecision::number >’ to ‘int64 {aka long long int}’ in assignment
What is the proper way to compare two int128_t numbers to see which one is greater?
EDIT: I am using std::max.
Your code (except for missing semicolons) compiles and runs without error.
However, according to your compiler message, I'm suspecting that in
int128_t a = Func_a(); // are you really sure it is int128_t?
the left-hand side is not a boost::multiprecision::int128_t, since the compiler says it is a int64.

Different results using same function from different libraries

Here is some code:
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
long long int a, b;
long long int c;
cin >> a >> b;
c = abs(a) + abs(b);
cout << c;
cout << endl;
}
Which supposed to return 1000000000000000002 when I input 1000000000000000000 and 2.
If I try to do it with cmath it will return 1000000000000000000, but if I use cstdlib it will return 1000000000000000002. Why is that even happening?
Also considering that I'm using cmath, shouldn't it work even more proper?
I'm using Linux Mint 18.2 64bit, Eclipse Platform.
The cmath version is a float one. So when you only have that one, you actually do the computation on floats and convert back into a long long at the end. Float precision not being enough to hold 18 digits, the +2 simply gets lost.
The cstdlib version is a integer one. Which gives the expected result.
As pointed out in the comments, in C++11, cmath also defines a version of abs that takes integers. However, “these overloads effectively cast x to a double before calculations (defined for T being any integral type)”.
I believe your compiler should give you a warning for the conversions if you use -Wall -Wextra or similar flags while only inclulding cmath.
If you are using g++, try to compile both versions with -Wconversion (or -Wfloat-conversion).
Note that the <cmath> version generates a warning:
main.cpp:14:7: warning: conversion to ‘long long int’ from ‘__gnu_cxx::__enable_if::__type {aka double}’ may alter its value [-Wfloat-conversion]
c = abs(a) + abs(b);
While the <cstdlib>version compiles without warnings.
That is because, in <cmath>, abs() is defined as1:
float abs(float);
double abs(double);
long double abs(long double);
While in <cstdlib> it is defined as1:
int abs(int);
long abs(long);
long long abs(long long);
1The integer versions of abs() are defined in <cmath> since C++17.

Is there a GCC warning for using symbols from the C library not through namespace std?

Consider the following (buggy) C++ code:
#include <cmath>
#include <cstdlib>
#include <iostream>
int main() {
if (abs(-0.75) != 0.75) {
std::cout << "Math is broken!\n";
return 1;
} else {
return 0;
}
}
This code is buggy because it calls abs (meaning ::abs) instead of std::abs. Depending on the implementation, ::abs might not exist, or it might be the C abs, or it might be an overload set including a version for double, like std::abs is.
With Clang on Linux, at least in my environment, it turns out to be the second option: C abs. This provokes two warnings, even without explicitly enabling any:
<source>:7:9: warning: using integer absolute value function 'abs' when argument is of floating point type [-Wabsolute-value]
if (abs(-0.75) != 0.75) {
^
<source>:7:9: note: use function 'std::abs' instead
if (abs(-0.75) != 0.75) {
^~~
std::abs
<source>:7:13: warning: implicit conversion from 'double' to 'int' changes value from -0.75 to 0 [-Wliteral-conversion]
if (abs(-0.75) != 0.75) {
~~~ ^~~~~
On GCC, I get different results in different environments and I haven’t yet figured out what details of the environment are relevant. The more common option, though, is also that it calls the C abs function. However, even with -Wall -Wextra -pedantic, it gives no warnings. I can force a warning with -Wfloat-conversion, but that gives too many false positives on the rest of my codebase (which perhaps I should fix, but that’s a different issue):
<source>: In function 'int main()':
<source>:7:18: warning: conversion to 'int' alters 'double' constant value [-Wfloat-conversion]
if (abs(-0.75) != 0.75) {
^
Is there a way to get a warning whenever I use a library function through the global namespace, when the version in namespace std is an overload?
Here's a solution. I'm not happy with it, but it might work for you:
namespace DontUseGlobalNameSpace {
// put all std functions here you want to catch
int abs(int x);
}
using namespace DontUseGlobalNameSpace;
Now, if you use abs() without qualification, you'll get a "symbol is ambiguous" error.
This is going to be difficult. The GCC <cmath> header simply includes <math.h>, #undefs its macros (just in case) and defines the C++ functions as inline functions which make some use of identifiers from <math.h>. Most of the functions in fact refer to compiler builtins: for instance, std::abs is defined using __builtin_abs and not ::abs.
Since <cmath> and your "buggy program" are all in the same translation unit, it's hard to see how the visibility could be separated: how the inline functions in <cmath> could be allowed to use <math.h> stuff, while your code wouldn't.
Well, there is the following way: <cmath> would have to be rewritten to provide its own locally scoped declarations for anything that it needs from <math.h> and not actually include that header.
What we can do instead is prepare a header file which re-declares the functions we don't want, with an __attribute__ ((deprecated)):
// put the following and lots of others like it in a header:
extern "C" int abs(int) throw () __attribute__ ((deprecated));
#include <cmath>
#include <cstdlib>
#include <iostream>
int main() {
if (abs(-0.75) != 0.75) {
std::cout << "Math is broken!\n";
return 1;
} else {
return 0;
}
}
Now:
$ g++ -Wall buggy.cc
buggy.cc: In function ‘int main()’:
buggy.cc:9:7: warning: ‘int abs(int)’ is deprecated [-Wdeprecated-declarations]
if (abs(-0.75) != 0.75) {
^~~
In file included from /usr/include/c++/6/cstdlib:75:0,
from buggy.cc:4:
/usr/include/stdlib.h:735:12: note: declared here
extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur;
^~~
buggy.cc:9:16: warning: ‘int abs(int)’ is deprecated [-Wdeprecated-declarations]
if (abs(-0.75) != 0.75) {
^
In file included from /usr/include/c++/6/cstdlib:75:0,
from buggy.cc:4:
/usr/include/stdlib.h:735:12: note: declared here
extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur;
^~~
A linker warning would be simpler. I tried that; the problem is that this test program doesn't actually generate an external reference to abs (even though there is an #undef abs in <cmath>). The call is being inlined, and so evades the linker warning.
Update:
Following up DanielH's comment, I have come up with a refinement of the trick which allows std::abs but blocks abs:
#include <cmath>
#include <cstdlib>
#include <iostream>
namespace proj {
// shadowing declaration
int abs(int) __attribute__ ((deprecated));
int fun() {
if (abs(-0.75) != 0.75) {
std::cout << "Math is broken!\n";
return 1;
} else {
return std::abs(-1); // must be allowed
}
}
}
int main() {
return proj::fun();
}
Simple namespaces can be used. Also, we don't need the deprecated attribute; we can just declare abs as an incompatible function, or a non-function identifier entirely:
#include <cmath>
#include <cstdlib>
#include <iostream>
namespace proj {
// shadowing declaration
class abs;
int fun() {
if (abs(-0.75) != 0.75) {
std::cout << "Math is broken!\n";
return 1;
} else {
return std::abs(-1); // must be allowed
}
}
}
int main() {
return proj::fun();
}
$ g++ -std=c++98 -Wall buggy.cc -o buggy
buggy.cc: In function ‘int proj::fun()’:
buggy.cc:10:18: error: invalid use of incomplete type ‘class proj::abs’
if (abs(-0.75) != 0.75) {
^
buggy.cc:7:9: note: forward declaration of ‘class proj::abs’
class abs;
^~~
buggy.cc:16:3: warning: control reaches end of non-void function [-Wreturn-type]
}
^
With this approach, we just need a list of names and dump them into some header that provides this:
int abs, fabs, ...; // shadow all of these as non-functions
I used -stdc++98 in the g++ command line to emphasizes that this is just old school C++ namespace semantics at work.
This code will let you detect whether the trap exists in a particular environment:
double (*)(double) = &::abs; // fails if you haven't included math.h, possibly via cmath
But it won't help you spot the places you fall into the trap.