c ++流负数转换

时间:2018-03-06 14:18:43

标签: c++ stream hex negative-integer

我遇到了一个问题,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);
}

3 个答案:

答案 0 :(得分:1)

long

这会将值int32_t读入long,然后将其转换为strtol。如果0xffffffff大于32位,则int32_t起作用并返回long,即4294967295,将其转换为LONG_MAX会产生-1。但这与从字符串中读取负数不同(如果int32_t是32位,则它不能按预期工作,而是返回0x7fffffff并将其转换为{{1 },这是int32_t cppint; ss >> std::hex >> cppint; )。

0xffffffff

这会尝试将值int32_t读入0xffffffff,但值strtol不适合该类型,因此读取该值失败(就像它失败了long 1}} strtol为32位时。

与您的int32_t cppint; long l; if (ss >> std::hex >> l) cppint = l; else // handle error ... 版本相当的近似值为:

0xffffffff

期望能够将值strtol读入带符号的32位整数是不合理的。 0xffffffff并且istreams不读取位模式,它们读取数字,并且数字{{1}}不适合带符号的32位整数。

答案 1 :(得分:0)

如果第一个十六进制位是f,那么c ++使它成为“大数字”:'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";
}

答案 2 :(得分:0)

问题是 unsigned 整数记录了十六进制表示法。 strtol是一个C函数,显然对负整数的十六进制表示更宽容,并在内部将字符串作为无符号值读取,然后将其重新解释为有符号值。但即使在C语言中,strtol未指定此类处理,也未指定无符号值的转换,无法在签名类型中表示 结果是实现定义的或引发实现定义的信号。 (摘自C11的草案1570 6.3.1.3 [转换]有符号和无符号整数)

它可能以这种方式工作,以免破坏大量的遗留代码,但C ++是一种更新的语言,并且实现者决定对十六进制表示更严格。