在c++中,位运算符在__int128上工作吗

9avjhtql  于 2023-01-10  发布在  其他
关注(0)|答案(2)|浏览(157)

我正在做一个项目,需要将__int128转换为uint8_t的向量。
我已经有了一个函数,可以将long转换为uint8_t向量。
我想做的事情是这样的:

__int128 data = 0x5bc5ddd975d34ed0b4f18b410e7d2480
addLong(data >> 64);
addLong(data & 0xFFFFFFFFFFFFFFFF);

我得到的结果是0000000000b4f18b41e7d2480
正如你所看到的,第二部分,64个较低有效字节被正确处理了,但是大多数字节没有被正确处理。你知道这是否可能吗?
做一些类似的事情:

std::cout << std::hex << (long)(data >> 48) << std::endl;

得到的结果是:b4f1。
这就是为什么我认为位运算符不适用于__int128。

p8ekf7hl

p8ekf7hl1#

不幸的是,gcc仍然不支持__int128/unsigned __int128常量(它们没有后缀,比如ll表示long long)。
您的示例应该生成一个警告,指出您的文本值被截断为long long值:
godbolt

warning: integer constant is too large for its type
    |     __int128 data = 0x5bc5ddd975d34ed0b4f18b410e7d2480;
    |

(if如果没有,则很可能已禁用-Wconversion-默认情况下已启用)
按位运算应该可以正常工作。
如果要使用标准库函数(std::abs,等等...)对于128位int,您需要确保您没有在严格的标准模式下编译(即-std=c++20),而是以标准的GNU方言(例如-std=gnu++20)(如果您根本不指定-std,则这是默认值)-否则int 128会为std::abswon't be available重载。

用户定义的文本

利用C20 constevaluser-defined literals可以为__int128构建自定义编译时文字。
(it可以通过用constexpr替换consteval来使用这个C
20之前的版本,但是在这种情况下,如果字面量有问题,就不会出现编译时错误)
虽然对于int解析来说,这是一个很大的样板,但是如果你的程序中有很多__int128常量,这可能是值得的:
godbolt

#include <stdexcept>
#include <cctype>
#include <bit>

namespace int128_literal {

    // determines the base of the given digit string
    // and increments str past it
    consteval int determine_base(const char*& str) {
        int base = 10;
        if(str[0] == '0') {
            if(str[1] == 'x' || str[1] == 'X') {
                // hexadecimal
                str += 2;
                base = 16;
            } else if(str[1] == 'b' || str[1] == 'B') {
                // binary
                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'
            ) {
                // octal
                str += 1;
                base = 8;
            } else if(str[1] == '\0') {
                // zero literal
                base = 8;
            } else {
                throw std::logic_error("unknown literal prefix!");
            }
        }

        return base;
    }

    // parses the given hexadecimal digit.
    // returns -1 for the digit separator (')
    consteval int parse_digit(char character) {
        switch(character) {
            case '\'':
                // digit separator
                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) {
        // nullptr
        if(!str)
            throw std::logic_error("nullptr!");

        bool is_negative = false;
        if(*str == '-') {
            str++;
            is_negative = true;
        }

        // determine base
        int base = determine_base(str);

        int parsed_digits = 0;
        unsigned __int128 value = 0;
        while(*str != '\0') {
            int digit = parse_digit(*str);
            
            // digit separator
            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;
            }

            // check if digit is allowed in current base
            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;
            // detect overflow during multiply
            if(next_value / base != value)
                throw std::logic_error("literal too large for unsigned __int128!");

            next_value += digit;
            // detect overflow during addition
            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!");
        }

        // negate two's complement
        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

// numeric literal:
__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;

// or as string literal:
__int128 a = "0x5bc5ddd975d34ed0b4f18b410e7d2480"_int128;
unsigned __int128 b = "340'282'366'920'938'463'463'374'607'431'768'211'455"_uint128;
bf1o4zei

bf1o4zei2#

位运算符起作用,但初始化不起作用。整数字面值不能大于long long
我建议为初始化添加一个helper函数(如果gcc中还没有的话):

#include <cstdint>
#include <iostream>

__int128 init_int128(std::int64_t high, std::uint64_t low) {
    return __int128(high) << 64 | low;
}

int main() {
    __int128 data = init_int128(0x5bc5ddd975d34ed0, 0xb4f18b410e7d2480);

    std::cout << std::hex;
    std::cout << static_cast<std::int64_t>(data >> 64) << '\n';
    std::cout << static_cast<std::uint64_t>(data) << '\n';
}

产出

5bc5ddd975d34ed0
b4f18b410e7d2480

相关问题