【问题标题】:Correct algorithm to convert binary floating point "1101.11" into decimal (13.75)?将二进制浮点“1101.11”转换为十进制(13.75)的正确算法?
【发布时间】:2013-09-21 23:00:57
【问题描述】:

我用 C 语言编写了一个程序,将浮点数 represented in binary (1101.11) 转换为十进制数 (13.75)。

但是,我似乎无法从算法中得到正确的值。

二进制浮点数转十进制的正确方法是什么?

我正在使用 Dev CPP 编译器(32 位)。算法定义如下:

void b2d(double p, double q )
{
   double rem, dec=0, main, f, i, t=0;

   /* integer part operation */    
   while ( p >= 1 )
   {
     rem = (int)fmod(p, 10);
     p = (int)(p / 10);
     dec = dec + rem * pow(2, t);
     t++;
   }

   /* fractional part operation */
   t = 1; //assigning '1' to use 't' in new operation
   while( q > 0 )
   {
     main = q * 10;
     q = modf(main, &i); //extration of frational part(q) and integer part(i)
     dec = dec+i*pow(2, -t);
     t++;
   }

   printf("\nthe decimal value=%lf\n",dec); //prints the final output
}

int main()
{
   double bin, a, f;

   printf("Enter binary number to convert:\n");
   scanf("%lf",&bin);

   /* separation of integer part and decimal part */
   a = (int)bin;
   f = bin - a;       
   b2d(a, f); // function calling for conversion

   getch();
   return 0;
}

【问题讨论】:

  • 这个问题并不真正适合 Stackoverflow 格式。它基本上是“我的代码中有一个错误,你能找到它吗?”而不是“这件事没有按预期工作”。尝试减少问题的特殊性并创建最小的测试用例。如果您可以稍微改一下,这里有一个非常有效的问题。
  • 您的问题是 1101.11 不能表示为 double。你得到(除非你有不常见的doubles)1101.109999999999899955582804977893829345703125。这会破坏你的算法。
  • I have gone through the code many time - 查看它,还是使用调试器进行调试?看看scanf() 读入bin实际 值是多少。我敢打赌,这不是完全 1101.11
  • @BiswajitPaul 也编辑了一点,希望这会更清楚:)
  • printf("%f", ...) 的默认精度为 6 位小数。如果在小数点后 6 位以下存在不精确(提示:有),您将不会看到这样的结果。扫描后立即尝试printf("%.24f\n", bin)'

标签: c floating-point floating-point-conversion


【解决方案1】:

正如您所相信的那样,您不会将“1101.11”读取为以二进制表示的浮点数。您将其读取为 以 10 为底的浮点数转换为 IEEE 双精度浮点值,然后然后尝试更改基数。

此中间步骤固有的不精确性是您出现问题的原因。

按照 Vicky 的建议,更好的方法是:

  1. 将“1101.11”读取为字符串或文本行
  2. 转换整数和小数部分(whole=b1101=13numerator=b11=3denominator=4
  3. 将这些重新组合成whole + numerator/denominator = 13.75

【讨论】:

  • 如果二进制数原本不是字符串格式,有什么办法可以将其转换为字符串?二进制数最初来自一个 fpga 块 say14 位,以 msb 作为符号位,接下来的 6 位是整数的大小,最后 6 位是小数的大小...
  • 您可能应该将此作为一个新问题提出 - 您会得到比评论中更好的答案。
【解决方案2】:

解决方案

以下将按预期工作:

输出:

➤ gcc bin2dec.c -lm -o bin2dec && bin2dec
1101.11 -> 13.750000
1101 -> 13.000000
1101. -> 13.000000
.11 -> 0.750000

代码(bin2dec.c):

#include <stdio.h>
#include <math.h>

double convert(const char binary[]){
  int bi,i;
  int len = 0;
  int dot = -1;
  double result = 0;

  for(bi = 0; binary[bi] != '\0'; bi++){
    if(binary[bi] == '.'){
      dot = bi;
    }
    len++;
  }
  if(dot == -1)
    dot=len;

  for(i = dot; i >= 0 ; i--){
    if (binary[i] == '1'){
      result += (double) pow(2,(dot-i-1));
    }
  }
  for(i=dot; binary[i] != '\0'; i++){
    if (binary[i] == '1'){
      result += 1.0/(double) pow(2.0,(double)(i-dot));
    }
  }
  return result;
}

int main()
{
   char  bin[] = "1101.11";
   char  bin1[] = "1101";
   char  bin2[] = "1101.";
   char  bin3[] = ".11";

   printf("%s -> %f\n",bin, convert(bin)); 
   printf("%s -> %f\n",bin1, convert(bin1)); 
   printf("%s -> %f\n",bin2, convert(bin2)); 
   printf("%s -> %f\n",bin3, convert(bin3)); 

   return 0;
}

说明

上面的代码首先找到数字中小数点的索引。

一旦知道了,它就会从该索引开始前后遍历字符串,将适当的值添加到result 变量中。

如果字符是1,则第一个循环从小数点向后走并累加 2 的幂。它将与小数点的距离作为 2 的幂,减去 1 以使索引正确。即,它会累积:

pow(2,<distance-from-decimal-point>)

当索引到达字符串的开头时循环停止。

第二个循环往前走直到字符串的末尾,处理小数部分as expected它也使用与索引的距离,但这次累积小数部分:

1/pow(2,<distance-from-decimal-point>)

得出的例子:

1101.11 = 1101 + 0.11

1101 = 1*2^3 + 1*2^2 + 0*2^1 + 1*2^0 = 8 + 4 + 0 + 1 = 13

0.11 = 1/(2^1) + 1/(2^2) = 0.5 + 0.25 = 0.75

1101.11 = 13.75

注意格式错误的输入。 “10gsh.9701072.67812”会给你一个结果。这不会意味着太多:)

【讨论】:

  • 感谢您的解释...我想知道这段代码是否可以用于检查负值?我确实尝试使用 11111101.11 的二进制字符串,它给出了 253.75 的输出...为了读取负值,我需要做哪些修改?如果二进制数最初不是字符串格式,有什么办法可以将其转换为字符串?二进制数最初来自一个 fpga 块 say14 位,以 msb 作为符号位,接下来的 6 位是整数的大小,最后 6 位是小数的大小...
【解决方案3】:

这段代码行为异常:我添加了一些简单的打印语句

  while(q>0)
  {
     double i;
     main=q*10.0;
     q=modf(main, &i); //extration of frational part(q) and integer part(i)
     cout << "main = " << main << " frac part " << q << " int part " << i << endl;
     cin.get();
     dec=dec+i*pow(2,-t);
     t++;
  }

当你输入 1101.11 时,显示如下输出:

Enter binary number to convert(e.g: 1101.11 which will be 13.75 in decimal):
1101.11
bin in main 1101.11
p  1101 q 0.11

//inside the above while loop code
main = 1.1 frac part 0.1 int part 1
main = 1 frac part 1 int part 0  //^^^^^Error, given main=1, it should output integer part 1, fraction part 0
main = 10 frac part 1 int part 9  //^^^^^same strange error here, it should exit while already

所以你得到了错误的结果。我用输入 1 分别测试了modf,它给出了正确的结果。

所以我的猜测是您将二进制数读取为双精度数,然后尝试将此双精度数转换为二进制数。尽管数字的精度表明它是1101.11,但可能在幕后发生了一些事情。正如@Useless 所建议的,您可能需要将数字读取为字符串,找出小数点之前和之后的子字符串. 然后将这两个部分分别转换为十进制。

猜你喜欢
  • 2014-11-24
  • 2017-02-23
  • 2015-10-11
  • 1970-01-01
  • 2021-09-10
  • 1970-01-01
  • 2014-03-01
  • 2016-06-02
  • 1970-01-01
相关资源
最近更新 更多