代码之家  ›  专栏  ›  技术社区  ›  user3780807

c++流负数转换

  •  4
  • user3780807  · 技术社区  · 7 年前

    我遇到了一个问题,C++试图读取一个以十六进制形式填充有符号整数的文本文件,并将其解析为向量。我使用了C++流来重定向变量(stream>>var),似乎负数的解析不正确-变量的值为0,并且设置了stream fail标志。

    如果我尝试使用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 回复  |  直到 7 年前
        1
  •  2
  •   Jonathan Wakely    7 年前
    int32_t cint;
    cint = strtol(ss.str().c_str(), NULL, 0);
    

    读取值 0xffffffff 变成一个 long ,然后将其转换为 int32_t 。如果 长的 大于32位 strtol 工程和回报 0xffffffff i、 e.4294967295,并将其转换为 int32\u t 生产-1。但这与从字符串中读取负数不同(如果 长的 如果是32位,则它不会像您期望的那样工作,而是返回 LONG_MAX 并将其转换为 int32\u t ,即 0x7fffffff )。

    int32_t cppint;
    ss >> std::hex >> cppint;
    

    这将尝试读取值 0xffffffff 变成一个 int32\u t 但是价值 0xffffffff 不适合该类型,因此读取值失败(就像 strtol公司 什么时候 长的 是32位)。

    与您的 strtol公司 版本为:

    int32_t cppint;
    long l;
    if (ss >> std::hex >> l)
      cppint = l;
    else
      // handle error ...
    

    期望能够读取值是不合理的 0xffffffff 转换为有符号32位整数。 strtol公司 iStream不读取位模式,而是读取数字 0xffffffff 不适用于有符号32位整数。

        2
  •  0
  •   alamoot j08691    7 年前

    如果第一个十六进制位为 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";
    }
    
        3
  •  0
  •   Serge Ballesta    7 年前

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

    它可能是这样工作的,以便不破坏成吨的遗留代码,但C++是一种较新的语言,实现者决定对十六进制表示更加严格。