【问题标题】:std::hex cannot process negative numbers?std::hex 不能处理负数?
【发布时间】:2022-01-06 19:08:59
【问题描述】:

我正在尝试使用 std::hex 从文件中读取十六进制整数。

0
a
80000000
...

这些整数是正数和负数。

似乎 std::hex 无法处理负数。我不明白为什么,也没有看到文档中定义的范围。

这是一个测试台:

#include <iostream>
#include <sstream>
#include <iomanip>

int main () {

  int i;
  std::stringstream ss;

  // This is the smallest number
  // That can be stored in 32 bits -1*2^(31)
  ss << "80000000";

  ss >> std::hex >> i;

  std::cout << std::hex << i << std::endl;

}

输出:

7fffffff

【问题讨论】:

  • 如何知道十六进制数已签名? 0x80000000 将溢出 int,假设是 32 位 int,然后所有的赌注都关闭了。好吧,也许不在 C++ 20 中,但我还没有用 20 做太多事情。
  • @user4581301 我想我想让 std::hex 推断出这个数字是负数,因为它正在读入一个 32 位整数。
  • 我想解决方案是读入 unsigned int,然后强制转换为signed。
  • 没有关于 cppreference 的好信息,我在标准中找不到任何说明 hex 不是无符号的东西。它只是将用于转换的基数设置为 16,然后走开。
  • @user4581301 非常感谢,如果您想将这些 cmets 作为答案,我很乐意接受。

标签: c++ hex iomanip


【解决方案1】:

设置std::hex 告诉流读取整数标记as though using std::scanf with the %X formatter%X reads into an unsigned integer,即使位模式适合,结果值也会溢出int。由于溢出,读取失败,无法信任i 的内容来保存预期值。旁注:i 将被设置为 0,如果编译为 C++11 或更新版本,或者与 c++11 之前的当前未指定值保持不变。

请注意,如果我们在读取后检查流状态,您应该总是这样做,我们可以看到读取失败:

#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdint> // added for fixed width integers.
int main () {

  int32_t i; //ensure 32 bit int
  std::stringstream ss;

  // This is the smallest number
  // That can be stored in 32 bits -1*2^(31)
  ss << "80000000";

  if (ss >> std::hex >> i)
  {
      std::cout << std::hex << i << std::endl;
  }
  else
  {
      std::cout << "FAIL! " <<  std::endl; //will execute this
  }
}

解决方案是,正如询问者在 cmets 中推测的那样,读入 unsigned int(如果 int 不是 32 位,uint32_t 以避免进一步的意外)。以下是代码的零惊喜版本,使用memcpy 将读取的确切位模式传输到i

#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdint> // added for fixed width integers.
#include <cstring> //for memcpy
int main () {

  int32_t i; //ensure 32 bit int
  std::stringstream ss;

  // This is the smallest number
  // That can be stored in 32 bits -1*2^(31)
  ss << "80000000";

  uint32_t temp;
  if (ss >> std::hex >> temp)
  {
      memcpy(&i, &temp, sizeof(i));// probably compiles down to cast
      std::cout << std::hex << i << std::endl;
  }
  else
  {
    std::cout << "FAIL! " <<  std::endl; 
  }
}

也就是说,暂时深入研究老式的 C 风格编码

  if (ss >> std::hex >> *reinterpret_cast<uint32_t*>(&i))
  {
    std::cout << std::hex << i << std::endl;
  }
  else
  {
    std::cout << "FAIL! " <<  std::endl; 
  }

违反了the strict aliasing rule,但是一旦 32 位 int 被强制使用 int32_t i;,我会很震惊地看到它失败。在最近的 C++ 标准中,这甚至可能是合法的,因为它是“类型相似”,但我仍在纠结这一点。

【讨论】:

    猜你喜欢
    • 2016-03-26
    • 1970-01-01
    • 2018-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-28
    • 2012-10-12
    相关资源
    最近更新 更多