【问题标题】:double to hex string & hex string to doubledouble 到十六进制字符串 & 十六进制字符串到 double
【发布时间】:2010-10-04 14:11:55
【问题描述】:

我要做的是将双精度转换为十六进制字符串,然后再转换回双精度。

以下代码将双精度字符串转换为十六进制字符串。

char * double2HexString(double a)
{
   char *buf = new char[17]; // double is 8-byte long, so we have 2*8 + terminating \0
   char *d2c;
   d2c = (char *) &a;
   char *n = buf;
   int i;
   for(i = 0; i < 8; i++)
   {
      sprintf(n, "%02X", *d2c++);
      n += 2;
   } 
   *(n) = '\0';
}

这似乎可行,但是,我不确定如何将生成的字符串转换回双精度。 请指教:)

【问题讨论】:

  • 您是要打印原始字节还是数字的十六进制表示?另外,您是否关心字符串本身(而不是代码)的可移植性?
  • 您更喜欢便携、易于编码/阅读还是快速的解决方案?
  • 我更喜欢易于编码和阅读的解决方案。我需要这个在程序之间传递一些参数。更具体一点——第一个程序生成另一个程序并将 2 个编码为十六进制字符串的双精度数传递给它。 Strager,“字符串本身的可移植性”是什么意思?
  • 如果在两个不同的硬件平台上使用它,可移植性可能会成为问题,因为您使用数据的二进制形式。 Big Endian/Little Endian machine, Network byte order and such things应该考虑在内。

标签: c++ c double hex


【解决方案1】:

我很惊讶没有人提出标准解决方案,即 ISO C99 标准中的 %a 格式说明符。

#include <iostream>
#include <string>
#include <stdio.h>

std::string double2hexastr(double d) {

  char buffer[25] = { 0 };

  ::snprintf(buffer, 25, "%A", d); // TODO Check for errors

  return buffer;
}

double hexastr2double(const std::string& s) {

  double d = 0.0;

  ::sscanf(s.c_str(), "%lA", &d); // TODO Check for errors

  return d;
}


int main() {

  std::cout << "0.1 in hexadecimal: " << double2hexastr(0.1) << std::endl;

  std::cout << "Reading back 0X1.999999999999AP-4, it is ";

  std::cout << hexastr2double("0X1.999999999999AP-4") << std::endl;

}

【讨论】:

  • @Jahid 该问题要求doublenot 用于long double。如果您希望为long double 执行此操作,则需要"%LA" 转换说明符。您现在可能想支持我的回答。
【解决方案2】:
char *doubleToRawString(double x) {
    // Assumes sizeof(long long) == 8.

    char *buffer = new char[32];
    sprintf(buffer, "%llx", *(unsigned long long *)&x);  // Evil!
    return buffer;
}

double rawStringToDouble(const char *s) {
    // Assumes sizeof(long long) == 8.

    double ret;
    sscanf(s, "%llx", (unsigned long long *)&ret);  // Evil!
    return ret;
}

【讨论】:

    【解决方案3】:
    char *doubleToRawString(double x) {
        const size_t bytesInDouble = 8;
    
        union {
            double value;
            unsigned char bytes[bytesInDouble];
        } u;
    
        u.value = x;
    
        char *buffer = new char[bytesInDouble * 2 + 1];
        unsigned char *input = u.bytes;
        char *output = buffer;
    
        for(int i = 0; i < bytesInDouble; ++i) {
            sprintf(output, "%02hhX", *input);
    
            ++input;
            output += 2;
        }
    
        return buffer;
    }
    
    double rawStringToDouble(const char *input) {
        const size_t bytesInDouble = 8;
    
        union {
            double value;
            unsigned char bytes[bytesInDouble];
        } u;
    
        unsigned char *output = u.bytes;
    
        for(int i = 0; i < bytesInDouble; ++i) {
            sscanf(input, "%02hhX", output);
    
            input += 2;
            ++output;
        }
    
        return u.value;
    }
    

    这使用了非标准的hh 修饰符。如果您不想使用它,请使用:

    unsigned int tmp = *input;
    sprintf(output, "%02X", tmp);
    
    unsigned int tmp;
    sscanf(input, "%02X", &tmp);
    *output = tmp;
    

    【讨论】:

      【解决方案4】:

      对于 MFC,将 double 转换为 CString :)

      CString MFCClass::DoubleToCString(double d, int beforKomma)
      {
          char a[17]="0123456789ABCDEF";
          CString str = _T("");
          double dInt=0,dPunkt=0;
          bool is_otr=0;
          if (d<0) {is_otr=1; d=-d;}
          dPunkt = modf(d, &dInt);
          //целая часть
          long ld = (long)dInt;
          long mask = 0xf;
          int it;
          while(ld>0)
          {
              it = ld&mask;
              ld = ld>>4;
              str.Insert(0,a[it]);
          };
          // дробная часть
          //если целая часть 0:
          if (str.GetLength()==0) str += _T("0");
          str += _T(".");
          for (int i=0; i<beforKomma; i++)
          {
              dPunkt*=16;
              dPunkt = modf(dPunkt, &dInt);
              str += a[(int)dInt];
          }
      
          if (is_otr) str.Insert(0,_T("-"));
          return (str);
      }
      

      -345.86783907228863 -> "-159.DE2" (beforKomma=3)

      【讨论】:

        【解决方案5】:

        说实话,使用 sprintf 很慢,但您可以使用 sscanf 恢复它,做几乎完全相同的事情。

        好吧,实际上,您必须将每两个字符复制到一个缓冲区字符串中,以便单独解码。我的第一次尝试,以下是不正确的:

        double hexString2Double(char *buf)
        {
          char *buf2 = new char[3];
          double a;
          char* c2d;
          c2d = (char *) &a;
          int i;
        
          buf2[2] = '\0'
        
          for(i = 0; i < 16; i++)
          {
            buf2[0] = *buf++;
            buf2[1] = *buf++;
            sscanf(buf2, "%X", c2d++);
          }
        
          return a;
        }
        

        你看,%X 被解码为一个 int,而不是一个字节。它甚至可能会起作用,具体取决于低端/高端问题,但它基本上已经坏了。所以,让我们尝试解决这个问题:

        double hexString2Double(char *buf)
        {
          char *buf2 = new char[3];
          double a;
          char* c2d;
          c2d = (char *) &a;
          int i;
          int decoder;
        
          buf2[2] = '\0'
        
          for(i = 0; i < 16; i++)
          {
            buf2[0] = *buf++;
            buf2[1] = *buf++;
            sscanf(buf2, "%X", &decoder);
            c2d++ = (char) decoder;
          }
        
          return a;
        }
        

        除非语法错误等,我认为这应该可行。

        【讨论】:

          【解决方案6】:

          几乎相同的程序应该做

          void hex2double(const char* buf, double& a)
          {
             char tmpbuf[3]={0};
             char *d2c;
             unsigned int tmp;
             d2c = (char *) &a;
             char *n = buf;
             int i;
             for(i = 0; i < 8; i++)
             {
                tmpbuf[0]=*buf++;
                tmpbuf[1]=*buf++;
                sscanf(tmpbuf, "%X", &tmp);
                *d2c++=tmp;
             }
          }
          

          又快又脏。

          但是请注意,这是在玩火。首先,您的十六进制字符串只能在具有相同双格式和相同字节序的机器上使用。其次,转换函数缺乏严格的别名规则。

          【讨论】:

            【解决方案7】:
            #include <stdio.h>
            main() {
              union double_ull_t {
                double d;
                unsigned long long u;
              } x;
              scanf("%lf",&x.d);
              printf("%016llX %lf\n",x.u,x.d);
              scanf("%016llX",&x.u);
              printf("%016llX %lf\n",x.u,x.d);
            }
            

            也许不是最有效的解决方案,但最容易编码。

            【讨论】:

            • 看起来 strager 用这种特殊的方法打败了我,而且实现更适用
            【解决方案8】:

            你想使用联合并避免这个坏习惯:

            char *d2c;

            d2c = (char *) &a;

            只是打印它还不错,当您尝试修改 d2c 然后使用 a 时,您就会遇到麻烦。 (对于共享相同(理论)内存的任何两个变量或指针(或数组)也是如此。

            union
            {
                double f;
                unsigned long ul;
            } myun;
            
            myun.f = a;
            printf("0x%lX",myun.ul);
            
            to go the other way (scanf is also a very dangerous function that should be avoided).
            
            myun.ul=strtoul(string,NULL,16);
            a=myun.f;
            

            【讨论】:

              【解决方案9】:

              我正在使用下面的函数,将双精度转换为十六进制。

              char * double2HexString(double a) 
              { 
                 char *buf = new char[17]; // double is 8-byte long, so we have 2*8 + terminating \0 
                 char *d2c; 
                 d2c = (char *) &a; 
                 char *n = buf; 
                 int i; 
                 for(i = 0; i < 8; i++) 
                 { 
                    sprintf(n, "%02X", *d2c++); 
                    n += 2; 
                 }  
                 *(n) = '\0'; 
              }  
              

              将 1.0 和 16.0 传递给此函数,观察到的返回值分别为 000000000000FF37 和 0000000000003040。我认为我们应该得到 1 和 10。

              【讨论】:

                猜你喜欢
                • 2014-03-07
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-02-26
                • 1970-01-01
                • 2023-04-08
                • 2020-11-07
                相关资源
                最近更新 更多