为什么在 C++ 表达式中使用无符号整数时会出现错误?
给出以下一段(伪 C++)代码:
float x=100, a=0.1;
unsigned int height = 63, width = 63;
unsigned int hw=31;
for (int row=0; row < height; ++row)
{
for (int col=0; col < width; ++col)
{
float foo = x + col - hw + a * (col - hw);
cout << foo << " ";
}
cout << endl;
}
在 (col - hw) 为负数的地方, foo 的值在数组的一半中被搞砸了。我认为因为 col 是 int 并且排在第一位,所以表达式的这一部分被转换为 int 并变为负数。不幸的是,显然事实并非如此,我得到了无符号值的溢出,但我不知道为什么。
我应该如何解决这个问题?对整个或部分表达式使用强制转换吗?什么类型的转换(C 风格或 static_cast<...>)?使用强制转换有任何开销吗(我需要它快速工作!)?
编辑:我将所有无符号整数更改为常规整数,但我仍然想知道为什么在这种情况下会出现溢出。
Given the following piece of (pseudo-C++) code:
float x=100, a=0.1;
unsigned int height = 63, width = 63;
unsigned int hw=31;
for (int row=0; row < height; ++row)
{
for (int col=0; col < width; ++col)
{
float foo = x + col - hw + a * (col - hw);
cout << foo << " ";
}
cout << endl;
}
The values of foo are screwed up for half of the array, in places where (col - hw) is negative. I figured because col is int and comes first, that this part of the expression is converted to int and becomes negative. Unfortunately, apparently it doesn't, I get an overflow of an unsigned value and I've no idea why.
How should I resolve this problem? Use casts for the whole or part of the expression? What type of casts (C-style or static_cast<...>)? Is there any overhead to using casts (I need this to work fast!)?
EDIT: I changed all my unsigned ints to regular ones, but I'm still wondering why I got that overflow in this situation.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
无符号整数实现无符号算术。无符号算术是模算术。所有值均以 2^N 为模进行调整,其中 N 是无符号类型值表示中的位数。
简而言之,无符号算术总是产生非负值。每次表达式应产生负值时,该值实际上会“环绕”2^N 并变为正值。
当您在[sub-]表达式中混合有符号和无符号整数时,无符号算术“获胜”,即计算在无符号域中执行。例如,当您执行
col - hw
时,它会被解释为(unsigned) col - hw
。这意味着对于col == 0
和hs == 31
您将不会得到-31
结果。相反,您将得到UINT_MAX - 31 + 1
,这通常是一个巨大的正值。话虽如此,我必须指出,在我看来,使用无符号类型来表示固有的非负值始终是一个好主意。事实上,实际上,C/C++ 程序中的大多数(或至少一半)整型变量应该具有无符号类型。您尝试在示例中使用无符号类型是有道理的(如果正确理解意图)。此外,我也会对
col
和row
使用unsigned
。但是,您必须记住无符号算术的工作方式(如上所述)并相应地编写表达式。大多数时候,可以重写表达式,使其不会跨越无符号范围的边界,即大多数时候不需要将任何内容显式转换为有符号类型。否则,如果您最终确实需要使用负值,则适当地转换为有符号类型应该可以解决问题。Unsigned integers implement unsigned arithmetic. Unsigned arithmetic is modulo arithmetics. All values are adjusted modulo 2^N, where N is the number of bits in the value representation of unsigned type.
In simple words, unsigned arithmetic always produces non-negative values. Every time the expression should result in negative value, the value actually "wraps around" 2^N and becomes positive.
When you mix a signed and an unsigned integer in a [sub-]expression, the unsigned arithmetic "wins", i.e. the calculations are performed in unsigned domain. For example, when you do
col - hw
, it is interpreted as(unsigned) col - hw
. This means that forcol == 0
andhs == 31
you will not get-31
as the result. Instead you wil getUINT_MAX - 31 + 1
, which is normally a huge positive value.Having said that, I have to note that in my opinion it is always a good idea to use unsigned types to represent inherently non-negative values. In fact, in practice most (or at least half) of integer variables in C/C++ the program should have unsigned types. Your attempt to use unsigned types in your example is well justified (if understand the intent correctly). Moreover, I'd use
unsigned
forcol
androw
as well. However, you have to keep in mind the way unsigned arithmetic works (as described above) and write your expressions accordingly. Most of the time, an expression can be rewritten so that it doesn't cross the bounds of unsigned range, i.e. most of the time there's no need to explicitly cast anything to signed type. Otherwise, if you do need to work with negative values eventually, a well-placed cast to signed type should solve the problem.如何制作
height
、width
和hw
带符号整数?让它们不签名你真正得到了什么?混合有符号和无符号整数总是会带来麻烦。至少乍一看,在这里使用无符号值似乎没有获得任何好处。所以,你不妨把它们都签名一下,省去麻烦。How about making
height
,width
, andhw
signed ints? What are you really gaining by making them unsigned? Mixing signed and unsigned integers is always asking for trouble. At first glance, at least, it doesn't look like you gain anything by using unsigned values here. So, you might as well make them all signed and save yourself the trouble.转换规则是向后的——当您混合同一类型的有符号和无符号版本时,有符号操作数将转换为无符号操作数。
You have the conversion rules backwards — when you mix signed and unsigned versions of the same type, the signed operand is converted to unsigned.
如果你希望它很快,你应该在开始循环之前对所有无符号值进行 static_cast 并使用它们的 int 版本而不是 unsigned int。您仍然可以要求输入未签名,然后将它们投射到算法中以保留函数所需的域。
If you want this to be fast you should static_cast all unsigned values before you start looping and use their int versions rather than unsigned int. You can still require inputs being unsigned and then just cast them on the way to your algorithm to retain the required domain for your function.
强制转换不会自动发生——未强制转换的算术仍然有它的用途。通常的示例是 int / int = int,即使不转换为 float 会丢失数据。我会使用signed int,除非由于INT_MAX太小而无法这样做。
Casts won't happen automatically- uncasted arithmetic still has it's uses. The usual example is int / int = int, even if data is lost by not converting to float. I'd use signed int unless it's impossible to do so because of INT_MAX being too small.