遗憾的是,gcc 仍然不支持 __int128
/ unsigned __int128
的字面量(就像 long long 没有后缀一样)。
你的示例应该会产生一个警告,指出你的字面值被截断为 long long
值:
godbolt
warning: integer constant is too large for its type
| __int128 data = 0x5bc5ddd975d34ed0b4f18b410e7d2480;
|
(如果您没有得到一个警告,那么您很可能已经禁用了-Wconversion
- 它默认是启用的)
按位运算应该没有问题。
如果您想要利用标准库函数(std::abs
等)来处理128位整数,您需要确保您不是在严格的标准模式下编译(即-std=c++20
),而是在标准的gnu方言下编译(即-std=gnu++20
)(如果您根本没有指定-std
,则这是默认设置)- 否则,std::abs
等的int128重载将不可用。
用户自定义字面量
利用 C++20 的 consteval
和 用户自定义字面量,可以为 __int128
构建自定义编译时字面量。
(在 C++20 之前,可以通过将 consteval
替换为 constexpr
来实现,但在这种情况下,如果字面量存在问题,您将无法获得编译时错误)
虽然解析 int 需要很多样板代码,但如果您的程序中有大量的 __int128
字面量,则可能值得这样做:
godbolt
#include <stdexcept>
#include <cctype>
#include <bit>
namespace int128_literal {
consteval int determine_base(const char*& str) {
int base = 10;
if(str[0] == '0') {
if(str[1] == 'x' || str[1] == 'X') {
str += 2;
base = 16;
} else if(str[1] == 'b' || str[1] == 'B') {
str += 2;
base = 2;
} else if(
str[1] == '0' || str[1] == '1' || str[1] == '2' ||
str[1] == '3' || str[1] == '4' || str[1] == '5' ||
str[1] == '6' || str[1] == '7'
) {
str += 1;
base = 8;
} else if(str[1] == '\0') {
base = 8;
} else {
throw std::logic_error("unknown literal prefix!");
}
}
return base;
}
consteval int parse_digit(char character) {
switch(character) {
case '\'':
return -1;
case '0':
return 0;
case '1':
return 1;
case '2':
return 2;
case '3':
return 3;
case '4':
return 4;
case '5':
return 5;
case '6':
return 6;
case '7':
return 7;
case '8':
return 8;
case '9':
return 9;
case 'a':
case 'A':
return 10;
case 'b':
case 'B':
return 11;
case 'c':
case 'C':
return 12;
case 'd':
case 'D':
return 13;
case 'e':
case 'E':
return 14;
case 'f':
case 'F':
return 15;
default:
throw std::logic_error("Unknown digit in literal!");
}
}
consteval unsigned __int128 parse(const char* str) {
if(!str)
throw std::logic_error("nullptr!");
bool is_negative = false;
if(*str == '-') {
str++;
is_negative = true;
}
int base = determine_base(str);
int parsed_digits = 0;
unsigned __int128 value = 0;
while(*str != '\0') {
int digit = parse_digit(*str);
if(digit == -1) {
if(parsed_digits == 0)
throw std::logic_error("digit separator not allowed at beginning of literal!");
else if(*(str + 1) == '\0')
throw std::logic_error("digit separator not allowed at end of literal!");
str++;
continue;
}
switch(base) {
case 2:
if(digit > 1)
throw std::logic_error("only 0-1 allowed for binary!");
break;
case 8:
if(digit > 7)
throw std::logic_error("only 0-7 allowed for octal!");
break;
case 10:
if(digit > 9)
throw std::logic_error("only 0-9 allowed for decimal!");
break;
}
unsigned __int128 next_value = value * base;
if(next_value / base != value)
throw std::logic_error("literal too large for unsigned __int128!");
next_value += digit;
if(next_value < value) {
throw std::logic_error("literal too large for unsigned __int128!");
}
value = next_value;
str++;
parsed_digits++;
}
if(parsed_digits == 0) {
throw std::logic_error("no digits in literal!");
}
if(is_negative) {
value = ~value + 1;
}
return value;
}
consteval unsigned __int128 operator""_uint128(const char* str)
{
return parse(str);
}
consteval unsigned __int128 operator""_uint128(const char* str, std::size_t)
{
return operator""_uint128(str);
}
consteval __int128 operator""_int128(const char* str)
{
unsigned __int128 value = parse(str);
return std::bit_cast<__int128>(value);
}
consteval __int128 operator""_int128(const char* str, std::size_t)
{
return operator""_int128(str);
}
}
using int128_literal::operator""_int128;
using int128_literal::operator""_uint128;
这样您就可以像下面这样编写__int128
/ unsigned __int128
文字:
godbolt
__int128 a = 0x5bc5ddd975d34ed0b4f18b410e7d2480_int128;
unsigned __int128 b = 340'282'366'920'938'463'463'374'607'431'768'211'455_uint128;
__int128 c = -0b11111111111111010101010101010101001010101010010101001_int128;
__int128 d = 075642412376_int128;
__int128 a = "0x5bc5ddd975d34ed0b4f18b410e7d2480"_int128;
unsigned __int128 b = "340'282'366'920'938'463'463'374'607'431'768'211'455"_uint128;
high
的上移不会溢出,那么一切都应该没问题。虽然我不太习惯使用这些扩展类型。也许我应该将high
和low
都设为无符号类型,然后让操作员在之后转换为有符号类型? - Ted Lyngmo