[英]Why does modulo division go wrong for mix of size_t and unsigned int in C++
Given a program给定一个程序
#include <iostream>
using namespace std;
int main()
{
const size_t DoW = 7;
const unsigned int DAYS_OF_WEEK = static_cast<unsigned int> (DoW);
unsigned int dayOfFirstDay = 0;
unsigned int _firstDayOfWeek = 1;
unsigned int diff = (DAYS_OF_WEEK+ (dayOfFirstDay - _firstDayOfWeek) ) % DAYS_OF_WEEK;
cout << "diff = (" << DAYS_OF_WEEK << " + (" << dayOfFirstDay << " - " << _firstDayOfWeek << ")) %" << DAYS_OF_WEEK
<< " = " << diff << endl;
return 0;
}
The output of that program is该程序的 output 是
diff = (7 + (0 - 1)) %7 = 6
which is expected.这是预期的。 But a modified program without
static_cast
但是没有
static_cast
的修改程序
#include <iostream>
using namespace std;
int main()
{
const size_t DAYS_OF_WEEK = 7;
unsigned int dayOfFirstDay = 0;
unsigned int _firstDayOfWeek = 1;
unsigned int diff = (DAYS_OF_WEEK+ (dayOfFirstDay - _firstDayOfWeek) ) % DAYS_OF_WEEK;
cout << "diff = (" << DAYS_OF_WEEK << " + (" << dayOfFirstDay << " - " << _firstDayOfWeek << ")) %" << DAYS_OF_WEEK
<< " = " << diff << endl;
return 0;
}
outputs输出
diff = (7 + (0 - 1)) %7 = 3
which is not expected.这是意料之中的。 Why?
为什么?
(Both programs are compiled with g++
9.3.0 on Ubuntu 64 Bit) (这两个程序都是在 Ubuntu 64 位上使用
g++
9.3.0 编译的)
It seems on your platform size_t
is 64-bit, and unsigned int
is 32-bit.在您的平台上似乎
size_t
是 64 位,而unsigned int
是 32 位。
There is no integral promotion to 64-bits 1 .没有对 64 位1的整体提升。 This is the danger of mixing 64-bit operands in expressions.
这就是在表达式中混合 64 位操作数的危险。
So a 32-bit wraparound of -1 remains as 4294967295 when converted to 64 bits.因此,当转换为 64 位时,-1 的 32 位环绕保持为 4294967295。
And we get 7 + 4294967295 (performed in 64 bits) = 4294967302 (no wraparound).我们得到 7 + 4294967295(以 64 位执行)= 4294967302(无环绕)。
4294967302 % 7 = 3 4294967302 % 7 = 3
1 Except for systems where ( unsigned
) int
itself is 64 bits, which is currently unlikely. 1除了 (
unsigned
) int
本身是 64 位的系统,这目前不太可能。
Such result can happen when size_t
has more width than unsigned int
.当
size_t
的宽度大于unsigned int
时,可能会发生这种结果。
The subtraction of unsigned int
and unsigned int
wraps around and results in unsigned int
. unsigned int
和unsigned int
的减法环绕并导致unsigned int
。 0 - 1
results in -1
, and it may become 0xffffffff
when unsigned int
is 4-byte long. 0 - 1
结果为-1
,当unsigned int
为 4 字节长时,它可能变为0xffffffff
。
Then, adding that with another unsigned int
will result in unsigned int
, so the result looks like normal subtraction and addition.然后,将其与另一个
unsigned int
相加将导致unsigned int
,因此结果看起来像正常的减法和加法。
On the other hand, adding with size_t
will have it calculate in size_t
domain, so truncation doesn't happen and the value 7 + 0xffffffff
will be divided instead of 7 - 1
.另一方面,与
size_t
相加将使其在size_t
域中计算,因此不会发生截断,并且值7 + 0xffffffff
将被除而不是7 - 1
。
Here is an example code to check the values before division:这是一个示例代码,用于在除法之前检查值:
#include <iostream>
#include <ios>
int main()
{
const size_t DoW = 7;
const unsigned int DAYS_OF_WEEK = static_cast<unsigned int> (DoW);
unsigned int dayOfFirstDay = 0;
unsigned int _firstDayOfWeek = 1;
size_t to_add = dayOfFirstDay - _firstDayOfWeek;
size_t diff_uint = DAYS_OF_WEEK+ (dayOfFirstDay - _firstDayOfWeek);
size_t diff_sizet = DoW+ (dayOfFirstDay - _firstDayOfWeek);
std::cout << "sizeof(unsigned int) = " << sizeof(unsigned int) << '\n';
std::cout << "sizeof(size_t) = " << sizeof(size_t) << '\n';
std::cout << std::hex;
std::cout << "to add : 0x" << to_add << '\n';
std::cout << "diff_uint : 0x" << diff_uint << '\n';
std::cout << "diff_sizet : 0x" << diff_sizet << '\n';
return 0;
}
Here is an example of output :这是output 的示例:
sizeof(unsigned int) = 4
sizeof(size_t) = 8
to add : 0xffffffff
diff_uint : 0x6
diff_sizet : 0x100000006
dayOfFirstDay - _firstDayOfWeek
is an unsigned int
. dayOfFirstDay - _firstDayOfWeek
是一个unsigned int
。 As _firstDayOfWeek
is greater than dayOfFirstDay
the value is an underflow and wrap around and becomes max value of unsigned int
.由于
_firstDayOfWeek
大于dayOfFirstDay
,该值是下溢并环绕并成为unsigned int
的最大值。 Let's call this value max_uint
.我们称这个值为
max_uint
。
On the other hand DAYS_OF_WEEK
is a size_t
which is probably a wider type than unsigned int
.另一方面
DAYS_OF_WEEK
是size_t
,它可能是比unsigned int
更宽的类型。 This means that DAYS_OF_WEEK + max_uint
is not overflowing.这意味着
DAYS_OF_WEEK + max_uint
没有溢出。 So you end-up computing max_uint % 7
.所以你最终计算
max_uint % 7
。 But max_uint % 7
is not equal to -1
...但是
max_uint % 7
不等于-1
...
Try it with less obfuscation:尝试减少混淆:
#include <stdio.h>
#include <stddef.h>
int main() {
printf("0u - 1u = %u\n", 0u - 1u);
printf("7u + (0u - 1u) = %u\n", 7u + (0u - 1u));
printf("7zu + (0u - 1u) = %zu\n", size_t{7} + (0u - 1u));
}
The output I get: output 我得到:
0u - 1u = 4294967295
7u + (0u - 1u) = 6
7zu + (0u - 1u) = 4294967302
As you can see, 0u - 1u
results in a wraparound.如您所见,
0u - 1u
导致环绕。 Adding this huge number to an unsigned int
results in another wraparound.将这个巨大的数字添加到
unsigned int
会导致另一个环绕。 Adding it to a size_t
doesn't as the entire value is representable.将其添加到
size_t
并不是因为整个值是可表示的。 For that reason, you get different results after the modulus operator.出于这个原因,在模运算符之后会得到不同的结果。
In the initializer expression of this declaration在此声明的初始化表达式中
unsigned int diff = (DAYS_OF_WEEK+ (dayOfFirstDay - _firstDayOfWeek) ) % DAYS_OF_WEEK;
the sub-expression (dayOfFirstDay - _firstDayOfWeek)
is equal to the maximum value of the type unsigned int
.子表达式
(dayOfFirstDay - _firstDayOfWeek)
等于unsigned int
类型的最大值。
Thus in this sub-expression when DAYS_OF_WEEK
has the type unsigned int
因此,当
DAYS_OF_WEEK
的类型为unsigned int
时,在此子表达式中
(DAYS_OF_WEEK+ (dayOfFirstDay - _firstDayOfWeek) )
an overflow occurs.发生溢出。
When DAYS_OF_WEEK
has the type size_t
when neither overflow occurs.当
DAYS_OF_WEEK
的类型为size_t
且均未发生溢出时。
This is the reason of different results.这就是结果不同的原因。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.