在 64 位可执行文件中执行 64 位计算

Perform 64 bit calculations in 64 bit executable

我正在将 MinGW64(带有 -m64 标志)与 Code::Blocks 一起使用,我想知道如何执行 64 位计算而不必将非常大的数字转换为 int64_t在乘以它之前。例如,这不会导致溢出:

int64_t test = int64_t(2123123123) * 17; //Returns 36093093091

没有强制转换,计算会像这样溢出:

int64_t test = 2123123123 * 17; //Returns 1733354723

VirusTotal 扫描确认我的可执行文件是 x64。

附加信息:OS 是 Windows 7 x64。

出于兼容性原因,即使在 64 位编译中,默认的 int 类型仍然是 32 位。

我猜"shortest"版本是在数字

上加上ll后缀
int64_t test = 2123123123ll * 17;

另一种方法是将数字存储在它们自己的 int64_t(或 long long)类型的变量中,然后将变量相乘。通常在一个程序中很少有 "magic-numbers" 硬编码到代码库中。

2123123123 是一个 int(通常是 32 位)。

添加一个 L 使其成为 long2123123123L(通常是 32 或 64 位,即使在 64 位模式下也是如此)。

添加另一个 L 使其成为 long long2123123123LL(64 位或更多,从 C++11 开始)。

请注意,您只需要为超过int 大小的常量添加后缀。积分转换将负责产生正确的结果*。

(2123123123LL * 17)  // 17 is automatically converted to long long, the result is long long

* 但请注意:即使表达式中的单个常量适合 int 整个操作 仍然会像

中那样溢出
(1024 * 1024 * 1024 * 10)

在那种情况下,您应该确保以足够的宽度执行算术(考虑运算符优先级):

(1024LL * 1024 * 1024 * 10)

- 将以 64 位执行所有 3 个操作,结果为 64 位。

由于您很可能处于 LP64 环境中,其中 int 仅为 32 位,因此您必须小心表达式中的文字常量。最简单的方法是养成在文字常量上使用正确后缀的习惯,因此您可以将上面的代码写成:

int64_t test = 2123123123LL * 17LL;

一些背景:

曾几何时,大多数计算机都有 8 位算术逻辑单元和 16 位地址总线。我们称它们为 8 位计算机。

我们了解到的第一件事是,没有任何现实世界的算术问题可以用 8 位来表示。这就像试图用黑猩猩的算术能力来推理 space 飞行。所以我们学会了写多字的加、乘、减、除序列。因为在大多数现实世界的问题中,问题的数值域都大于255。

我们曾短暂地拥有 16 位计算机(同样的问题适用,65535 不足以建模)然后很快,芯片中内置了 32 位算术逻辑。渐渐地,地址总线迎头赶上(20 位、24 位、32 位,如果设计师觉得奢侈的话)。

然后发生了一件有趣的事情。我们中的大多数人不再需要编写多字算术序列。事实证明,大多数 (tm) 现实世界整数问题可以用 32 位(最多 40 亿)表示。

然后我们开始以比以往更快的速度生成更多数据,并且我们意识到需要寻址更多内存。 64位电脑最终成为常态

但是,大多数现实世界的整数算术问题仍然可以用 32 位表示。 40 亿对于大多数事情来说是一个很大(足够)的数字。

因此,大概通过统计分析,您的编译器编写者决定在您的平台上,int 最有用的大小是 32 位。对于 32 位算法(我们从第一天起就需要),任何更小的值都将是低效的,而任何更大的值将浪费 space/registers/memory/cpu 个周期。

在 c++(和 c)中表达整数文字会产生一个 int - 环境的自然算术大小。在今天,这几乎总是一个 32 位值。

C++ 规范说两个整数相乘得到一个整数。如果不是,那么将两个整数相乘将需要产生一个长整数。但是,两个多头的乘积会是多少?长长?好的,这是可能的。现在,如果我们将它们相乘怎么办?长长长长?

就是这样。

int64_t x = 1 * 2; 将执行以下操作:

  1. 取值1的整数(32位)
  2. 取值2的整数(32位)
  3. 将它们相乘,将结果存储在一个整数中。如果算术溢出,那就这样吧。那是你的瞭望台。
  4. 将结果整数(无论现在是什么)转换为 int64(可能在您的系统上是 long int

简而言之,不。在问题的代码片段中拼出至少一个操作数的类型没有捷径可走。当然,您可以指定文字。但是不能保证你系统上的along long(LL字面后缀)和int64_t是一样的。如果你想要一个int64_t,并且你希望代码是可移植的,你必须把它拼出来。

物有所值:

在 post-c++11 的世界中,所有关于额外击键和非 DRYness 的担忧都会消失:

绝对是 int64:

auto test = int64_t(2123123123) * 17;

绝对长长:

auto test = 2'123'123'123LL * 17;

肯定是 int64,肯定是用一个(可能变窄,但没关系)long long 初始化的:

auto test = int64_t(36'093'093'091LL);

编辑:文字常量(A.K.A。幻数)不受欢迎,所以最好的方法是使用符号常量(const int64_t value = 5)。有关详细信息,请参阅 What is a magic number, and why is it bad?您最好不要阅读此答案的其余部分,除非您出于某种奇怪的原因真的想使用幻数。

此外,你可以从#include <cstdint>中使用intptr_tuintprt_t让编译器选择使用int还是__int64

对于那些偶然发现这个问题的人,数字末尾的 `LL` 可以解决问题,但不推荐这样做,正如 Richard Hodges 告诉我的那样,`long long` 可能并不总是 64 位,并且可以增加未来的规模,尽管这不太可能。有关详细信息,请参阅 Richard Hodge 的回答及其评论。

可靠的方法是将 `using QW = int_64t;` 放在顶部并使用 `QW(5)` 而不是 `5LL`。

我个人认为应该有一个选项来定义所有 64 位文字,而不必向它们添加任何后缀或函数,并在必要时使用“int32_t(5)”,因为某些程序不受此更改的影响。示例:只使用数字进行正常计算,而不是依靠整数溢出来完成它的工作。问题是从 64 位到 32 位,而不是从 32 到 64,因为前 4 个字节被截断了。