EDIT In the actual example, it appears possible that negative overflow can happen, I've also added an example to demonstrate the error there
I'm using C 20 and trying to convert a library which relies on signed integer overflow in Java and C# into C code. I'm also trying to generate the tables it uses at compile time, and allow those to be available at compile time.
In my code I get errors in reference to code that looks like this (Minimal example to reproduce the error, the solution to this will solve my problem as well):
#include <iostream>
constexpr auto foo(){
std::int64_t a = 2;
std::int64_t very_large_constant = 0x598CD327003817B5L;
std::int64_t x = a * very_large_constant;
return x;
}
int main(){
std::cout << foo() << std::endl;
return 0;
}
https://godbolt.org/z/TvM45vd8d
Negative overflow version
#include <iostream>
constexpr auto foo(){
std::int64_t a = -2;
std::int64_t very_large_constant = 0x598CD327003817B5L;
std::int64_t x = a * very_large_constant;
return x;
}
int main(){
std::cout << foo() << std::endl;
return 0;
}
https://godbolt.org/z/7zoE9r18E
I get 12905529061151879018 is out side of range representable by long long and -12905529061151879018 respectively.
I understand that undefined behavior here is not allowed, I also recognize that GCC and MSVC do not error here, and you can put a flag to make clang compile this anyway. But what am I supposed to do to actually solve this issue with out switching compilers or applying the flag to ignore invalid constexpr?
Is there some way I can define the behavior I expect and want to happen here?
CodePudding user response:
You cannot do this with signed integers. However, there are some things you can rely on in C 20:
Unsigned integer overflow is well-defined.
Signed integers are required to be represented as 2's complement.
Conversions between corresponding sized and unsigned integers preserve the bitpattern.
So you can do all of your overflow-based math using explicitly unsigned types and literals, then cast them to signed values when you need to. This conversion is required to leave the bits unchanged.
CodePudding user response:
Signed integers have two's complement layout in any implementation that you could name. It's also guaranteed to use two' s complement layout since C 20.
This means that you can perform your math on unsigned
integers and get well-defined overflow behavior that matches what you want your signed
integers to do.
#include <iostream>
#include <bit>
constexpr auto foo(){
std::uint64_t a = 2;
std::uint64_t very_large_constant = 0x598CD327003817B5L;
std::uint64_t x = a * very_large_constant;
return static_cast<std::int64_t>(x);
}