【问题标题】:Converting double to hexadecimal - Code review将双精度转换为十六进制 - 代码审查
【发布时间】:2012-04-03 14:44:23
【问题描述】:

我有以下代码,它采用双精度值并将其转换为十六进制表示,反之亦然。我想知道它是否有任何潜在的问题——我是否忽略了一些东西。

double hex_to_double2(string &hexString)
{
    unsigned char byte_string[sizeof(double)];
    int number;

    int j = 0;

    for(int i = 0; i < hexString.size() ; i += 2)
    {
        sscanf(&hexString[i], "%02x", &number);
        byte_string[j] = (unsigned char)number;
        ++j;
    }

    double p = (double&)byte_string;
    return p;
}

std::string double_to_hex_string(double d)
{
    unsigned char *buffer = (unsigned char*)&d;
    int bufferSize = sizeof(double);

    char converted[bufferSize * 2];

    int j = 0;
    for(int i = 0 ; i < bufferSize ; ++i)
    {
        sprintf(&converted[j*2], "%02X", buffer[i]);
        ++j;
    }
    string hex_string(converted);
    return hex_string;
}

它似乎工作正常。但是有人告诉我char converted[bufferSize * 2]; 应该是char converted[bufferSize * 2 + 1];

是这样吗?

【问题讨论】:

  • 如果您有 C99 兼容的 printf 实现,您可以考虑使用 "%a" 格式字符串来检查您的代码。

标签: c++ ieee-754


【解决方案1】:

是的,converted 的大小应该是bufferSize * 2 + 1。您需要在字符串末尾有一个零或空字符的空间。

sprintf 在字符串末尾插入一个空字符,但您没有为它腾出空间;这意味着它正在破坏缓冲区之后的内存中的一些值。很难预测这可能会产生什么副作用。它可能会弄乱其他变量的值,可能会使您的程序崩溃,或者它可能看起来工作正常。

【讨论】:

    【解决方案2】:

    是的。每次调用 sprintf() 都会在缓冲区中写入三个字符:两个十六进制数字和一个用于终止字符串的空值。最后一个将在当前实现的缓冲区之外写入一个。

    【讨论】:

      【解决方案3】:

      double 放入char byte_string [sizeof(double)] 的另一个问题是对齐。这种相当棘手的策略称为类型双关。您也想在该函数中分配sizeof(double)*2 字节,然后在缓冲区中间构造一个对齐的指针。

      一般来说,这种“抛砖引玉”是个坏主意,但你恰好是在规则的右边,几乎。您只想将 double 转换为 char 数组,而不是相反。此外,scanf 可以为您做更多的工作,并消除循环。

      double hex_to_double2(string &hexString)
      {
          double value;
      
          sscanf( hexString.c_str(), "%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
                  (char*) & value, // note, this is a reinterpret_cast
                  (char*) & value + 1,
                  (char*) & value + 2,
                  (char*) & value + 3,
                  (char*) & value + 4,
                  (char*) & value + 5,
                  (char*) & value + 6,
                  (char*) & value + 7 );
      
          return p;
      }
      

      您只能使用从 POD 类型(普通旧数据或 C 兼容)到 char 的这种指针类型转换。

      我将把相反的方式作为练习:)。

      【讨论】:

        【解决方案4】:

        这是我用来将最大为 255 的两个单独的双精度数转换为十六进制(firstNumber 和 secondNumber)的非常简单的方法:

        std::string hexCodes = "0123456789abcdef";
        
        std::stringstream finalResult;
        
        finalResult << hexCodes.at(floor(firstNumber/ 16)) << hexCodes.at(firstNumber- (16 * (floor(firstNumber/ 16))));
        
        finalResult << hexCodes.at(floor(secondNumber/ 16)) << hexCodes.at(secondNumber- (16 * (floor(secondNumber/ 16))));
        
        std::string finalString = finalResult.str();
        

        我不是很擅长编写代码,在搜索谷歌搜索这个问题的结果后,我没有找到任何解决方案,所以写了这个并且它工作......到目前为止

        【讨论】:

          【解决方案5】:

          是的,字符数组(也称为 C 字符串)通常应该以 '\0' 结尾。这在 C 中用作长度指示器。

          char converted[bufferSize * 2 + 1];
          converted[bufferSize*2] = '\0';
          or
          std::memset(converted, 0, bufferSize*2+1);
          

          【讨论】:

            【解决方案6】:

            ...任何潜在的问题...

            hex_to_double2 不会检查字符串是否适合您的 byte_string 缓冲区:您应该提前检查 hexString.size(),或者在 for 循环中添加退出条件。

            您还返回了一个指向本地缓冲区的指针,当hex_to_double2 返回时,它超出了范围 - 不要这样做。它很可能会通过工作来欺骗你……直到它坏掉。也许只是返回 std::vector&lt;unsigned char&gt; - 这会照顾缓冲区的生命周期并且您可以使用 push_back 修复您的 for 循环。

            有人告诉我char converted[bufferSize * 2] 应该是char converted[bufferSize * 2 + 1]

            是的,sprintf 附加了一个 nul 终止符,因此您的输出缓冲区应该更大。但是,既然您无论如何都在使用std::string,为什么不将您的十六进制数字格式化为std::ostringstream?同样,这将解决您的缓冲区大小问题并为您管理内存。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-05-05
              • 2011-04-03
              • 1970-01-01
              • 1970-01-01
              • 2014-02-07
              • 1970-01-01
              • 2019-08-13
              相关资源
              最近更新 更多