C++流负数转换
c++ stream negative number conversion
我遇到了一个问题,C++ 试图读取一个文本文件,其中包含十六进制形式的有符号整数并将它们解析为向量。我使用 C++ 流到变量重定向 (stream >> var),似乎负数没有被正确解析——变量得到值 0,并且设置了流失败标志。
如果我尝试使用 strtol() 函数转换字符串,结果与预期一致。同样,如果我尝试首先将流重定向到一个无符号整数,然后将变量转换为有符号整数,结果再次正确并且没有报告流错误。
我在 Debian 9.1 (x64) 上使用 gcc 6.3.0,运行 在 Xeon E5-2643 v3 系统上。
还有其他人遇到过这个问题吗?我希望转换的工作方式与 strtol 函数相同,并且不会报告任何流错误。我是否遗漏了一些流设置/忘记调用某些函数或在此处设置一些标志?
如有任何建议,我们将不胜感激。
下面附有一个演示此问题的示例 C++ 程序。
#include <iostream>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstdint>
int main()
{
const char* minus_one = "0xffffffff";
std::stringstream ss;
ss << minus_one;
std::cout << "input string : " << ss.str() << "\n"; // outputs "0xffffffff"
// C-style conversion
int32_t cint;
cint = strtol(ss.str().c_str(), NULL, 0);
std::cout << "strtol conv : " << cint << " (" << std::hex << cint << ")\n"; // outputs "-1 (ffffffff)"
std::cout << std::dec;
// C++-style conversion
int32_t cppint;
ss >> std::hex >> cppint;
std::cout << std::dec << "ssextr conv : " << cppint << " (" << std::hex << cppint << ")\n"; // outputs "0 (0)" <- ERROR
std::cout << std::dec;
if (ss.fail()) std::cout << "Error converting number.\n";
// C++-style conversion with cast
uint32_t cppuint;
int32_t cppint2;
ss.clear();
ss.str(minus_one);
ss >> std::hex >> cppuint;
cppint2 = (int32_t)cppuint;
std::cout << std::dec << "ssextr cast conv: " << cppint2 << " (" << std::hex << cppint2 << ")\n"; // outputs "-1 (0xffffffff)"
std::cout << std::dec;
if (ss.fail()) std::cout << "Error converting number.\n";
exit(EXIT_SUCCESS);
}
如果第一个十六进制位是 f
,则 c++ 将其设为 "large number":'0x7fffffff'。似乎 c++ 不想将其表示为负数。
像这样:
const char* minus_one = "0xf0000000"; //ssextr conv : 2147483647 (7fffffff)
std::stringstream ss;
ss << minus_one;
// C++ style conversion
int32_t cppint;
ss >> std::hex >> cppint;
std::cout << std::dec << "ssextr conv : " << cppint << " (" << std::hex << cppint << ")\n";
std::cout << std::dec;
if (ss.fail()) {
std::cout << "Error converting number.\n";
}
int32_t cint;
cint = strtol(ss.str().c_str(), NULL, 0);
这会将值 0xffffffff
读入 long
,然后将其转换为 int32_t
。如果 long
大于 32 位,则 strtol
起作用并且 returns 0xffffffff
即 4294967295,并将其转换为 int32_t
会产生 -1。但这与从字符串中读取负数不同(如果 long
是 32 位,则它不会像您预期的那样工作,而是 returns LONG_MAX
并将其转换到 int32_t
,即 0x7fffffff
).
int32_t cppint;
ss >> std::hex >> cppint;
这尝试将值 0xffffffff
读入 int32_t
但值 0xffffffff
不适合该类型,因此读取值失败(就像它失败一样strtol
当 long
是 32 位时)。
更接近于您的 strtol
版本的是:
int32_t cppint;
long l;
if (ss >> std::hex >> l)
cppint = l;
else
// handle error ...
期望能够将值 0xffffffff
读入带符号的 32 位整数是不合理的。 strtol
和 istream 不读取位模式,它们读取数字,而数字 0xffffffff
不适合带符号的 32 位整数。
问题是 无符号 整数的十六进制表示法已记录。 strtol
是一个 C 函数,显然更能容忍负整数的十六进制表示,并在内部将字符串读取为无符号值,然后 re-interprets 将其读取为有符号值。但即使在 C 中,对于 strtol
和无法用有符号类型表示的无符号值的转换 也未指定这样的处理
结果是 implementation-defined 或发出 implementation-defined 信号 。 (来自 C11 6.3.1.3 [转换] 有符号和无符号整数的草案 1570)
很可能它以这种方式工作是为了不破坏大量遗留代码,但 C++ 是一种更新的语言,实现者已决定对十六进制表示更加严格。
我遇到了一个问题,C++ 试图读取一个文本文件,其中包含十六进制形式的有符号整数并将它们解析为向量。我使用 C++ 流到变量重定向 (stream >> var),似乎负数没有被正确解析——变量得到值 0,并且设置了流失败标志。
如果我尝试使用 strtol() 函数转换字符串,结果与预期一致。同样,如果我尝试首先将流重定向到一个无符号整数,然后将变量转换为有符号整数,结果再次正确并且没有报告流错误。
我在 Debian 9.1 (x64) 上使用 gcc 6.3.0,运行 在 Xeon E5-2643 v3 系统上。
还有其他人遇到过这个问题吗?我希望转换的工作方式与 strtol 函数相同,并且不会报告任何流错误。我是否遗漏了一些流设置/忘记调用某些函数或在此处设置一些标志?
如有任何建议,我们将不胜感激。
下面附有一个演示此问题的示例 C++ 程序。
#include <iostream>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstdint>
int main()
{
const char* minus_one = "0xffffffff";
std::stringstream ss;
ss << minus_one;
std::cout << "input string : " << ss.str() << "\n"; // outputs "0xffffffff"
// C-style conversion
int32_t cint;
cint = strtol(ss.str().c_str(), NULL, 0);
std::cout << "strtol conv : " << cint << " (" << std::hex << cint << ")\n"; // outputs "-1 (ffffffff)"
std::cout << std::dec;
// C++-style conversion
int32_t cppint;
ss >> std::hex >> cppint;
std::cout << std::dec << "ssextr conv : " << cppint << " (" << std::hex << cppint << ")\n"; // outputs "0 (0)" <- ERROR
std::cout << std::dec;
if (ss.fail()) std::cout << "Error converting number.\n";
// C++-style conversion with cast
uint32_t cppuint;
int32_t cppint2;
ss.clear();
ss.str(minus_one);
ss >> std::hex >> cppuint;
cppint2 = (int32_t)cppuint;
std::cout << std::dec << "ssextr cast conv: " << cppint2 << " (" << std::hex << cppint2 << ")\n"; // outputs "-1 (0xffffffff)"
std::cout << std::dec;
if (ss.fail()) std::cout << "Error converting number.\n";
exit(EXIT_SUCCESS);
}
如果第一个十六进制位是 f
,则 c++ 将其设为 "large number":'0x7fffffff'。似乎 c++ 不想将其表示为负数。
像这样:
const char* minus_one = "0xf0000000"; //ssextr conv : 2147483647 (7fffffff)
std::stringstream ss;
ss << minus_one;
// C++ style conversion
int32_t cppint;
ss >> std::hex >> cppint;
std::cout << std::dec << "ssextr conv : " << cppint << " (" << std::hex << cppint << ")\n";
std::cout << std::dec;
if (ss.fail()) {
std::cout << "Error converting number.\n";
}
int32_t cint;
cint = strtol(ss.str().c_str(), NULL, 0);
这会将值 0xffffffff
读入 long
,然后将其转换为 int32_t
。如果 long
大于 32 位,则 strtol
起作用并且 returns 0xffffffff
即 4294967295,并将其转换为 int32_t
会产生 -1。但这与从字符串中读取负数不同(如果 long
是 32 位,则它不会像您预期的那样工作,而是 returns LONG_MAX
并将其转换到 int32_t
,即 0x7fffffff
).
int32_t cppint;
ss >> std::hex >> cppint;
这尝试将值 0xffffffff
读入 int32_t
但值 0xffffffff
不适合该类型,因此读取值失败(就像它失败一样strtol
当 long
是 32 位时)。
更接近于您的 strtol
版本的是:
int32_t cppint;
long l;
if (ss >> std::hex >> l)
cppint = l;
else
// handle error ...
期望能够将值 0xffffffff
读入带符号的 32 位整数是不合理的。 strtol
和 istream 不读取位模式,它们读取数字,而数字 0xffffffff
不适合带符号的 32 位整数。
问题是 无符号 整数的十六进制表示法已记录。 strtol
是一个 C 函数,显然更能容忍负整数的十六进制表示,并在内部将字符串读取为无符号值,然后 re-interprets 将其读取为有符号值。但即使在 C 中,对于 strtol
和无法用有符号类型表示的无符号值的转换 也未指定这样的处理
结果是 implementation-defined 或发出 implementation-defined 信号 。 (来自 C11 6.3.1.3 [转换] 有符号和无符号整数的草案 1570)
很可能它以这种方式工作是为了不破坏大量遗留代码,但 C++ 是一种更新的语言,实现者已决定对十六进制表示更加严格。