如果我的编译器不支持它们,如何在C或C ++中添加和减去128位整数?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如果我的编译器不支持它们,如何在C或C ++中添加和减去128位整数?相关的知识,希望对你有一定的参考价值。

我正在为128位数字的长流写一个压缩器。我想将数字存储为差异 - 仅存储数字之间的差异而不是数字本身,因为我可以将差异打包在更少的字节中,因为它们更小。

但是,对于压缩,我需要减去这些128位值,对于解压缩,我需要添加这些值。我的编译器的最大整数大小是64位宽。

任何人有任何想法有效地做到这一点?

答案

如果您只需要加法和减法,并且您已经拥有二进制形式的128位值,那么库可能很方便,但并不是绝对必要的。这个数学很容易做到。

我不知道你的编译器对64位类型使用了什么,所以我将使用INT64和UINT64来表示有符号和无符号的64位整数。

class Int128
{
public:
    ...
    Int128 operator+(const Int128 & rhs)
    {
        Int128 sum;
        sum.high = high + rhs.high;
        sum.low = low + rhs.low;
        // check for overflow of low 64 bits, add carry to high
        if (sum.low < low)
            ++sum.high;
        return sum;
    }
    Int128 operator-(const Int128 & rhs)
    {
        Int128 difference;
        difference.high = high - rhs.high;
        difference.low = low - rhs.low;
        // check for underflow of low 64 bits, subtract carry to high
        if (difference.low > low)
            --difference.high;
        return difference;
    }

private:
    INT64  high;
    UINT64 low;
};
另一答案

看看GMP

#include <stdio.h>
#include <gmp.h>

int main (int argc, char** argv) {
    mpz_t x, y, z;
    char *xs, *ys, *zs;
    int i;
    int base[4] = {2, 8, 10, 16};

    /* setting the value of x in base 10 */
    mpz_init_set_str(x, "100000000000000000000000000000000", 10);

    /* setting the value of y in base 16 */
    mpz_init_set_str(y, "FF", 16);

    /* just initalizing the result variable */
    mpz_init(z);

    mpz_sub(z, x, y);

    for (i = 0; i < 4; i++) {
        xs = mpz_get_str(NULL, base[i], x);
        ys = mpz_get_str(NULL, base[i], y);
        zs = mpz_get_str(NULL, base[i], z);

        /* print all three in base 10 */
        printf("x = %s
y = %s
z = %s

", xs, ys, zs);

        free(xs);
        free(ys);
        free(zs);
    }

    return 0;
}

输出是

x = 10011101110001011010110110101000001010110111000010110101100111011111000000100000000000000000000000000000000
y = 11111111
z = 10011101110001011010110110101000001010110111000010110101100111011111000000011111111111111111111111100000001

x = 235613266501267026547370040000000000
y = 377
z = 235613266501267026547370037777777401

x = 100000000000000000000000000000000
y = 255
z = 99999999999999999999999999999745

x = 4ee2d6d415b85acef8100000000
y = ff
z = 4ee2d6d415b85acef80ffffff01
另一答案

我完全偶然地偶然发现了这个相对较老的帖子,我认为有必要详细说明Volte之前的猜想,以便为没有经验的读者带来好处。

首先,128位数的有符号范围是-2127到2127-1,而不是最初规定的-2127到2127。

其次,由于有限算术的循环性质,两个128位数之间所需的最大差值为-2127到2127-1,其存储先决条件为128位,而不是129.尽管(2127-1) - ( - 2127)= 2128-1明显大于我们的最大2127-1正整数,算术溢出总是确保任何两个n位数之间的最近距离总是在0到2n-1的范围内,因此隐含地-2n- 1至2n-1-1。

为了澄清,让我们首先研究一个假设的3位处理器如何实现二进制加法。例如,请考虑下表,该表描述了3位整数的绝对无符号范围。

0 = 000b 1 = 001b 2 = 010b 3 = 011b 4 = 100b 5 = 101b 6 = 110b 7 = 111b ---> [溢出时循环回000b]

从上表可以看出:

001b(1)+ 010b(2)= 011b(3)

显而易见的是,使用其数字补码添加任何这些数字总是产生2n-1:

010b(2)+ 101b([2的补数] = 5)= 111b(7)=(23-1)

由于在添加两个n位数导致(n + 1)位结果时发生循环溢出,因此,将这些数字中的任何一个与其数字补码+ 1相加将始终产生0:

010b(2)+ 110b([补码2] + 1)= 000b(0)

因此,我们可以说[n的补码+ 1 = -n,因此n + [n的补码] + 1 = n +( - n)= 0.此外,如果我们现在知道n + [n的补] + 1 = 0,然后n + [n - x] + 1的补数必须= n - (nx)= x。

将其应用于我们原来的3位表会产生:

0 = 000b = [0的补码] + 1 = 0 1 = 001b = [7的补数] + 1 = -7 2 = 010b = [6的补数] + 1 = -6 3 = 011b = [5的补数] + 1 = -5 4 = 100b = [4的补数] + 1 = -4 5 = 101b = [3的补数] + 1 = -3 6 = 110b = [2的补数] + 1 = -2 7 = 111b = [1的补码] + 1 = -1 ---> [溢出时循环回000b]

我们现在有2n个n位模式可以无缝地服务于正0到2n-1和负0到 - (2n) - 表示抽象是正面的,负面的还是两者的组合所暗示的有符号二进制补码算法。 1个范围,当需要时。事实上,所有现代处理器都采用这样的系统,以便为加法和减法操作实现通用的ALU电路。当CPU遇到i1 - i2减法指令时,它在i2内部执行[complement + 1]运算,然后通过加法电路处理操作数,以计算i1 + [i2的补码] + 1.除了额外的进位/符号XOR门控溢出标志,有符号和无符号加法,以及暗示减法,都是隐含的。

如果我们将上表应用于Volte原始回复中提供的输入序列[-2n-1,2n-1-1,-2n-1],我们现在能够计算以下n位差分:

嘉宾#1: (2 N-1-1) - ( - 2n-1)= P - ( - 4)= P + 4 = (1)= h = 111 b

嘉宾#A: (N-1-N) - (2 N-1-1)= (-4) - p =( - 4)+(x)= (H)= 1 = 001b

从我们的种子-2n-1开始,我们现在能够通过顺序应用上述每个差异来重现原始输入序列:

(-2n -1)+(客人#1)= (-4)+ h = p = 2 N-1

(2 N-1-1)+(访客#A)= P +(h)=( - 4)= -2 n -1个

您当然希望对这个问题采用更哲学的方法,并猜测为什么2n个循环序列数将需要超过2n个循环序列差分?

Taliadon。

另一答案

Boost 1.53现在包括multiprecision:

#include <boost/multiprecision/cpp_int.hpp>
#include <iostream>

// Requires Boost 1.53 or higher
// build: g++ text.cpp

int main()
{
    namespace mp = boost::multiprecision;

    mp::uint128_t a = 4294967296;
    mp::uint256_t b(0);
    mp::uint512_t c(0);

    b = a * a;
    c = b * b;

    std::cout << "c: " << c << "
";
    return 0;
}

输出:

./a.out
c: 340282366920938463463374607431768211456
另一答案

关于大整数数学有很多文献。您可以使用其中一个免费提供的库(请参阅链接),也可以自行编辑。虽然我应该警告你,对于比加法和减法(和移位)更复杂的事情,你需要使用非平凡的算法。

要添加和减去,可以创建一个包含两个64位整数的类/结构。您可以使用简单的学校数学来进行加法和减法。基本上,用铅笔和纸做加法或减法,仔细考虑进行/借用。

搜索大整数。顺便说一下最新版本的VC ++,IntelC ++和GCC编译器都有128位整数类型,虽然我不确定它们是否像你想要的那样容易访问(它们的目的是与sse2 / xmms寄存器一起使用)。

另一答案

TomsFastMath有点像GMP(如上所述),但它是公共领域,并且从一开始就设计得非常快(它甚至包含x86,x86-64,ARM,SSE2,PPC32和AVR32的汇编代码优化) 。

另一答案

还值得注意的是:如果目标仅仅是通过预处理来改进数字流的压缩,那么预处理流不必由精确的算术差异组成。您可以使用XOR(^)代替+-。优点是128位XOR与64位器件上的两个独立XOR完全相同,因此它既简单又高效。

以上是关于如果我的编译器不支持它们,如何在C或C ++中添加和减去128位整数?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用cmake和Android NDK在C ++中加载线程支持

如何在C#中清除事件订阅?

支持64位应用程序的要求

如何在 C 中使用 /dev/random 或 urandom?

如何使用 CMake 在 MinGW 上激活 c++11 支持?

带有 GCC 的 C/C++:将资源文件静态添加到可执行文件/库