为什么减法会溢出static_cast?
why does subtraction overflow with static_cast?
我理解 s1.size() - s2.size()
在 s2
较大时下溢,因为它是 unsigned
的减法。
为什么将它们之一转换为 int
不会导致整数减法?
为什么铸造整个东西给我正确的结果?我希望它评估括号内的内容,然后下溢会给出一个大数字,然后转换为 int
不会有什么不同。我错过了什么?
#include <iostream>
#include <string>
using std::cout;
using std::cin;
using std::endl;
using std::string;
bool isShorter(const string &s1, const string &s2) {
return (static_cast<int>(s1.size()) - s2.size() < 0) ? true : false; // underflows
//return (static_cast<int>(s1.size() - s2.size()) < 0) ? true : false; // this works
}
int main() {
string s, t;
getline(cin, s);
getline(cin, t);
cout << "s: " << s << endl;
cout << "t: " << t << endl;
cout << "printing shorter string of the two..." << endl;
cout << ((isShorter(s, t)) ? s : t) << endl;
}
当你这样做时
static_cast<int>(s1.size()) - s2.size()
您将 s1.size()
转换为 int
,然后当您从中减去 s2.size()
时,int
被提升为与 s2.size()
相同的类型,并且然后它被减去。这意味着你仍然有无符号整数减法,因为它永远不会是负数,所以它会环绕到一个更大的数字。和 s1.size() - s2.size()
.
没什么区别
你和
一样
static_cast<int>(s1.size() - s2.size())
加上可能的有符号整数溢出的额外好处,这是未定义的行为。您仍在进行无符号整数减法,因此如果 s1
小于 s2
则您将返回一个大数。
你需要做的是将s1.size()
和s2.size()
都转换为有符号整数类型,以获得有符号整数减法。这可能看起来像
static_cast<ptrdiff_t>(s1.size()) - static_cast<ptrdiff_t>(s2.size())
现在如果 s1.size()
小于 s2.size()
,你实际上会得到一个负数。
需要注意的是,所有这些都可以通过使用小于运算符来避免。您的函数可以重写为
bool isShorter(const string &s1, const string &s2)
{
return s1.size() < s2.size();
}
恕我直言,这更容易阅读和理解。
将 "one of them" 转换为 int
后,您将进行混合 string::size_type
和 int
的算术运算。在这种混合中,无符号类型与 int
具有相同的等级或更高,这意味着无符号类型仍然 "wins":您的 int
被隐式转换回 string::size_type
并且计算在 string::size_type
的域中执行。您对 int
的转换实际上被忽略了。
同时,将结果转换为 int
意味着您正在尝试转换不适合 int
范围的值。这种情况下的行为是实现定义的。在现实生活中的 2's-complement 实现中,看到一个简单的表示截断并不罕见,它会产生 "correct" 结果。不过这不是一个好方法。
如果要将此减法作为带符号的减法执行,则必须将两个 操作数转换为带符号类型,确保目标带符号类型可以表示这两个值。
(理论上,您可以只将一个操作数转换为有符号类型,但为此您需要选择一种可以代表整个 string::size_type
范围的类型。)
我理解 s1.size() - s2.size()
在 s2
较大时下溢,因为它是 unsigned
的减法。
为什么将它们之一转换为 int
不会导致整数减法?
为什么铸造整个东西给我正确的结果?我希望它评估括号内的内容,然后下溢会给出一个大数字,然后转换为 int
不会有什么不同。我错过了什么?
#include <iostream>
#include <string>
using std::cout;
using std::cin;
using std::endl;
using std::string;
bool isShorter(const string &s1, const string &s2) {
return (static_cast<int>(s1.size()) - s2.size() < 0) ? true : false; // underflows
//return (static_cast<int>(s1.size() - s2.size()) < 0) ? true : false; // this works
}
int main() {
string s, t;
getline(cin, s);
getline(cin, t);
cout << "s: " << s << endl;
cout << "t: " << t << endl;
cout << "printing shorter string of the two..." << endl;
cout << ((isShorter(s, t)) ? s : t) << endl;
}
当你这样做时
static_cast<int>(s1.size()) - s2.size()
您将 s1.size()
转换为 int
,然后当您从中减去 s2.size()
时,int
被提升为与 s2.size()
相同的类型,并且然后它被减去。这意味着你仍然有无符号整数减法,因为它永远不会是负数,所以它会环绕到一个更大的数字。和 s1.size() - s2.size()
.
你和
一样static_cast<int>(s1.size() - s2.size())
加上可能的有符号整数溢出的额外好处,这是未定义的行为。您仍在进行无符号整数减法,因此如果 s1
小于 s2
则您将返回一个大数。
你需要做的是将s1.size()
和s2.size()
都转换为有符号整数类型,以获得有符号整数减法。这可能看起来像
static_cast<ptrdiff_t>(s1.size()) - static_cast<ptrdiff_t>(s2.size())
现在如果 s1.size()
小于 s2.size()
,你实际上会得到一个负数。
需要注意的是,所有这些都可以通过使用小于运算符来避免。您的函数可以重写为
bool isShorter(const string &s1, const string &s2)
{
return s1.size() < s2.size();
}
恕我直言,这更容易阅读和理解。
将 "one of them" 转换为 int
后,您将进行混合 string::size_type
和 int
的算术运算。在这种混合中,无符号类型与 int
具有相同的等级或更高,这意味着无符号类型仍然 "wins":您的 int
被隐式转换回 string::size_type
并且计算在 string::size_type
的域中执行。您对 int
的转换实际上被忽略了。
同时,将结果转换为 int
意味着您正在尝试转换不适合 int
范围的值。这种情况下的行为是实现定义的。在现实生活中的 2's-complement 实现中,看到一个简单的表示截断并不罕见,它会产生 "correct" 结果。不过这不是一个好方法。
如果要将此减法作为带符号的减法执行,则必须将两个 操作数转换为带符号类型,确保目标带符号类型可以表示这两个值。
(理论上,您可以只将一个操作数转换为有符号类型,但为此您需要选择一种可以代表整个 string::size_type
范围的类型。)