#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<int> v = {1, 2, 3, 4, 5, 6, 7};
int i = -4;
cout << i << endl;
cout << v.size() << endl;
cout << i % v.size() << endl;
cout << -4 % 7 << endl;
}
上面的代码打印出来了。
-4
7
5
-4
谁能解释一下为什么?i % v.size()
印花 5
而不是 -4
? 我猜测它与以下内容有关 vector.size()
,但不知道根本原因是什么。先谢谢你了。
的操作数 %
经历 常规算术转换 以使它们达到一个共同的类型,然后再进行分割。 如果操作数是 int
和 size_t
那么 int
转换为 size_t
.
如果 size_t
是32位,那么 -4
将成为 4294967292
然后表达式的结果是 4294957292 % 7
这其实 0
.
如果 size_t
是64位,那么 -4
将成为18,446,744,073,709,551,612,而这一结果将是 % 7
是 5
你也看到了。
所以实际上我们可以从这个输出中看出,你的系统有64位的size_t。
在C++中,模数运算符的定义是这样的,所以对于以下情况是真的 都 整数,除了b==0。
(a/b)*b + a%b == a
所以它必须和整数除法保持一致, 从C++ 11开始, 即使是负数也要截断为零. 因此,即使是负数,一切都很好定义。
然而,在你的例子中,你有一个有符号的无符号除法(因为.size()返回无符号),通常的signedunsigned规则适用。这意味着在这种情况下,所有的参数都会在操作执行之前被转换为无符号数(参见Ruslan的评论)。
所以-4被转换为无符号(并成为一个非常大的数字),然后进行modulo运算。
你也可以看到这一点,因为在任何整数除法的定义中,5都不是-4模数7的正确答案(3才是正确的)。
用C和C++的算术规则并不直观。
因为 v.size
返回 size_t
.
cout << -4 % size_t(7) << endl; // 5
你看一下 负值模数运算器
UPD:和 Signed-int-modulo-unsigned-int-produce-nonsense-results(无意义的结果)
这是因 v.size()
,这是一个无符号类型。由于整数的提升,这意味着结果也将被视为无符号类型,虽然 i
是一个有符号的类型。
我假设你是在64位上编译的。这意味着除了升级为无符号类型外,结果也将是64位的类型。unsigned long long
. 一步一步来。
unsigned long long _i = (unsigned long long)-4; // 0xFFFFFFFFFFFFFFFC!
unsigned long long result = _i % (unsigned long long)7; // 5
因为你大概想保存这些东西的签名性 i
在这种情况下,只需投下 v.size()
到已签署的类型,以防止升级到未签署的类型。i % (int)v.size()
会给 -4
.
从cppreference上看 通常的算术转换 和 C++标准
否则(签名性不同)。如果无符号类型的转换等级大于或等于有符号类型的等级,那么有符号类型的操作数将隐式转换为无符号类型。
-4
是 signed
和 7
是 size_t
这是 unsigned
型,所以 -4
转换为 unsigned
先进行模数,再进行模数。
有了这样的想法,如果你把它分解开来,你就会立刻明白发生了什么。
size_t s = -4; // s = 18446744073709551612 on a 64 bit system
size_t m = 7;
std::cout << s % m << '\n'; //5
对于32位系统来说,结果可能是不同的.
cout << -4 % 7 << endl;
仍然打印 -4
. 为什么呢?因为这两种类型的 -4
和 7
是 int
.
C++标准§5.13.2.3 整数文字的类型
整数字词的类型是表8中列表中的第一个类型,对应于它的可选整数后缀,它的值可以用它来表示。一个整数字词是一个prvalue。
表8:整数字的类型 无后缀。
int
long int
long long int
所以: -4
和 7
都是 int
在这种情况下,因此modulo的结果是 -4
.