【问题标题】:C: convert a real number to 64 bit floating point binaryC:将实数转换为 64 位浮点二进制
【发布时间】:2014-10-17 01:10:05
【问题描述】:

我正在尝试编写将实数转换为 64 位浮点二进制的代码。为此,用户输入一个实数(例如,547.4242),程序必须输出一个 64 位浮点二进制。

我的想法:

  • 标志部分很简单。
  • 程序转换整数部分(上例为 547)并将结果存储在一个 int 变量中。然后,程序转换小数部分(上例为.4242)并将结果存储到数组中(数组的每个位置存储“1”或“0”)。

这就是我卡住的地方。总而言之,我有:“整数部分 = 1000100011”(int 类型)和“小数部分 = 0110110010011000010111110000011011110110100101000100”(数组)。

我该如何继续?

【问题讨论】:

  • 你说你将它存储为双精度,但它看起来像二进制......不确定你到底在问什么
  • 双 x = (双) y; ?
  • @Noctis 对不起,我的意思是一个 int 变量。
  • 这很容易得到几乎正确的结果,但如果我没记错的话,要获得绝对正确的转换需要做很多工作。
  • 一个典型的 64 位浮点二进制可能具有类似“100...(300 个零)00.0”的值。将该实数的整数部分转换为intlonglong long 等肯定会导致截断。 “实数的整数部分”-> int 您的想法提出的方法远没有限制。建议 1)另一种方法 2)并发布您的代码。

标签: c floating-point binary converter fractions


【解决方案1】:

以下代码用于根据 IEEE754 表示法确定浮点数的内部表示。此代码是在 Turbo c++ ide 中编写的,但您可以轻松转换为通用 ide。

#include<conio.h>
#include<stdio.h>

void decimal_to_binary(unsigned char);

union u
{
    float f;
    char c;
};

int main()
{
    int i;
    char*ptr;
    union u a;

    clrscr();
    printf("ENTER THE FLOATING POINT NUMBER : \n");
    scanf("%f",&a.f);

    ptr=&a.c+sizeof(float);

    for(i=0;i<sizeof(float);i++)
    {
        ptr--;
        decimal_to_binary(*ptr);
    }

    getch();
    return 0;
}

void decimal_to_binary(unsigned char n)
{
    int arr[8];
    int i;
    //printf("n = %u  ",n);

    for(i=7;i>=0;i--)
    {
        if(n%2==0)
            arr[i]=0;
        else
            arr[i]=1;
        n/=2;
    }

    for(i=0;i<8;i++)
        printf("%d",arr[i]);
    printf(" ");
}

更多详情请点击here

【讨论】:

  • 在我看来,这与 OP 的要求相反。
  • @RudyVelthuis 你是对的。我想做相反的事情。
【解决方案2】:

诀窍是将值视为整数,因此将547.4242 读取为无符号长整型(即64 位或更多),即5474242,计算“.”后的位数,在这种情况下 4. 现在您的值比应有的值大 10^4。因此,您将 5474242 浮动(作为双精度或长双精度)并除以 10^4。

十进制到二进制的转换看似简单。当你的位数超过浮点数时,它就必须四舍五入。当您的数字多于 64 位整数所容纳的数字时,会更有趣——注意尾随零是特殊的——并且您必须决定是否舍入(以及浮动时会发生什么舍入)。然后是处理 E+/-99。然后,当您最终除以(或乘) 10^n 时,您有 (a) 另一个潜在的舍入,以及 (b) 大 10^n 没有精确在您的浮动中表示的问题点——这是另一个错误来源。 (对于 E+/-99 表格,最后一步可能需要超过 10^300。)

享受吧!

【讨论】:

  • 如果我输入 547.4242 并将其读取为 int 我会丢失小数部分 (0.4242),对吗?
  • 他的意思是你省略小数点'.',然后把数字547.4242读成整数,即读成5474242,同时记住小数点后面有多少位。然后将整数除以 10^x,其中 x 是小数位数。一定要进行浮点除法。
  • 另一种查看方式是将小数点向右移动。对于您移动小数点的每个数字,您当然是将数字乘以 10。因此,当您拥有完整的数字时,您可以将其转换为合适的浮点格式,然后除以所需的 10 的幂恢复其真实价值。 (你可以取小数点后的第一个数字,除以 10 并将其添加到整数部分,然后下一个数字,除以 100,将其添加,等等。但是这些操作中的每一个都会添加另一个舍入错误!)
【解决方案3】:

为了将所有可能的十进制表示正确地四舍五入到最接近的double,您需要大整数。仅使用 C 中的基本整数类型将使您重新实现大整数算术。这两种方法中的每一种都是可能的,关于每种方法的更多信息如下:

  1. 对于第一种方法,您需要一个大整数库:GMP 是一个很好的方法。有了这么大的整数库,您可以将示例 123.456E78 之类的输入处理为整数 123456 * 1075 并开始想知道 [253 中的 M 值是多少…… 254) 和 [-1022 … 1023] 中的 P 使 (M / 253) * 2P 最接近这个数字。这个问题可以用大整数运算来回答,按照this blog post中描述的步骤(总结:首先确定P。然后使用除法计算M)。完整的实现必须处理次正规数和无穷大(inf 是返回指数大于 +1023 的数字的任何十进制表示的正确结果)。

  2. 第二种方法,如果您不想包含或实现一个完整的通用大整数库,仍然需要在表示大数的 C 整数数组上实现一些基本操作。此implementation 中的函数decfloat() 表示以109 为底的大数,因为这简化了从初始十进制表示到内部表示的转换,即x 的数组x

【讨论】:

  • 我不认为大多数 atof() 实现在内部使用某种 BigInteger 或 BigDecimal。 ISTM 认为 OP 的“真实”是一个字符串,但我可能是错的。
  • @RudyVelthuis 您能否在一个不使用大整数解析1.0E300 的知名库中引用atof 的一种实现?大多数库都使用 David M. Gay 的代码,这里有一篇关于 Glibc 实现的文章:exploringbinary.com/how-glibc-strtod-works,我已经链接到 Musl 的。这些已经是多数,这取决于你如何计算,但我对这个主题很感兴趣,我希望看到没有某种大整数的 one 实现(有效。如果它产生,我不感兴趣结果在正确结果的十个 ULP 内)。
  • 什么是有信誉的,什么不是?我非常怀疑大多数atof 实现使用大整数。
  • @RudyVelthuis 好的,请引用一个 atof 实现,它没有来自 any libc 的某种大整数实现。
  • 我刚刚看了你的链接。他们使用 160-192 位整数。如果这就是你对大整数的意思,那么我同意。但对我来说,大整数要大得多。
【解决方案4】:

以下是基本的转换。足以让 OP 开始。

OP 的“实数的整数部分”--> int 限制太多。最好将整个字符串简单地转换为像uintmax_t 这样的大整数。注意小数点'.' 并在扫描时考虑溢出。

此代码不处理指数或负数。由于有限的整数ui 或最后的num = ui * pow10(expo),它可能在最后一位左右关闭。它处理大多数溢出情况。

#include <inttypes.h>

double my_atof(const char *src) {
  uintmax_t ui = 0;
  int dp = '.';
  size_t dpi;
  size_t i = 0;
  size_t toobig = 0;
  int ch;
  for (i = 0; (ch = (unsigned char) src[i]) != '\0'; i++) {
    if (ch == dp) {
      dp = '\0';  // only get 1 dp
      dpi = i;
      continue;
    }
    if (!isdigit(ch)) {
      break; // illegal character
    }
    ch -= '0';
    // detect overflow
    if (toobig || 
        (ui >= UINTMAX_MAX / 10 && 
        (ui > UINTMAX_MAX / 10 || ch > UINTMAX_MAX % 10))) {
      toobig++;
      continue;
    }
    ui = ui * 10 + ch;
  }
  intmax_t expo = toobig;
  if (dp == '\0') {
    expo -= i - dpi - 1;
  }

  double num;
  if (expo < 0) {
    // slightly more precise than: num = ui * pow10(expo);
    num = ui / pow10(-expo);
  } else {
    num = ui * pow10(expo);
  }
  return num;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-17
    • 2016-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多