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

Perform 64 bit calculations in 64 bit executable

本文关键字:64位 计算 执行 可执行文件      更新时间:2023-10-16

我正在使用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。

附加信息:操作系统为Windows 7 x64。

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

我想"最短"的版本是在数字

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

另一种方法是将数字存储在它们自己的int64_t(或long long)类型的变量中,并将变量相乘。通常情况下,在程序中将许多"神奇数字"硬编码到代码库中是很少见的。

一些背景:

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

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

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

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

然后我们开始以比以往更快的速度产生更多的数据,我们意识到需要处理更多的内存。64位计算机最终成为标准。

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

所以,大概是通过统计分析,你的编译器编写者决定在你的平台上,int类型最有用的大小是32位。任何更小的值对于32位算术运算来说都是低效的(这是我们从第一天开始就需要的),任何更大的值都会浪费空间/寄存器/内存/cpu周期。

在c++(和c)中表示整数字面值会产生int——环境的自然算术大小。现在,它几乎总是一个32位的值。

c++规范规定两个int型相乘得到一个int型。如果没有,那么两个int相乘就会得到一个long。那么两个长期债券相乘的收益是多少呢?一个长长的长?好吧,这是可能的。如果把它们相乘呢?一个长长的长长的长长的?

就是这样。

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

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

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

无论如何:

在c++11之后的世界里,所有关于额外击键和非干燥的担忧都将消失:

绝对是int64:

auto test = int64_t(2123123123) * 17;

a long long:

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

肯定是int64,肯定是用long(可能是窄化的,但没关系)long:

初始化的。
auto test = int64_t(36'093'093'091LL);

由于您很可能处于LP64环境中,其中int只有32位,因此您必须小心表达式中的文字常量。要做到这一点,最简单的方法是养成在文字常量上使用适当后缀的习惯,因此您可以将上面的代码写成:

int64_t test = 2123123123LL * 17LL;

2123123123int(通常为32位)。

添加L使其成为long: 2123123123L(通常为32位或64位,即使在64位模式下)。

添加另一个L使其成为long long: 2123123123LL(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位的结果。

编辑:文字常量(又称幻数)是不受欢迎的,所以最好的方法是使用符号常量(const int64_t value = 5)。看看什么是神奇的数字,为什么它不好?了解更多信息。你最好不要读这个答案的其余部分,除非你真的因为一些奇怪的原因想要使用魔法数字。

也可以使用#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个字节被切断了。