【问题标题】:Concatenating binary numbers连接二进制数
【发布时间】:2018-07-16 17:36:37
【问题描述】:

我正在尝试编写一个程序,该程序将采用以 10 为底的浮点数并将其以 2 为底的小数部分转换。在以下代码中,我打算将我的转换函数调用为 printf,并格式化输出;我遇到的问题在于我的 fra_binary() ,我无法找出分别返回由每轮转换结果组成的整数的最佳方法(连接)。这是我现在所做的(代码没有优化,因为我还在处理它):

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

int fra_binary(double fract) ;

int main() 
{
    long double n ;
    double fract, deci ;
    printf("base 10 :\n") ;
    scanf("%Lf", &n) ;
    fract = modf(n, &deci) ;
    int d = deci ;
    printf("base 2: %d.%d\n", d, fra_binary(fract)) ;

    return(0) ;
}

int fra_binary(double F) 
{
    double fl ;
    double decimal ;
    int array[30] ;

    for (int i = 0 ; i < 30 ; i++) {
        fl = F * 2 ;
        F = modf(fl, &decimal) ;
        array[i] = decimal ;
        if (F == 0) break ;
    }

    return array[0] ;
}

显然这会部分返回所需的输出,因为我需要将整个数组连接为一个 int 或 char 来显示我需要的一系列 1 和 0。所以在每一轮,我想使用我处理的数字的小数部分作为二进制数来连接(1 + 0 = 10 而不是 1)。我该怎么做? 希望这是有道理的!

【问题讨论】:

  • 通过返回该数组(或者更确切地说是指向其第一个元素的指针),您有一个非常清楚的 undefined behavior 案例。是时候找一本好书或教程,了解变量的范围和生命周期了。
  • 哦,如果您声明一个函数返回一种类型,然后将其定义为返回另一种类型,那将不会很好地工作,并且是编译器警告或错误的一个来源。
  • 谢谢@Someprogrammerdude,但正如我在原帖中所说,我留下“返回数组”表示我打算使用 for 循环的串联结果。在我的代码中,您会看到我已将循环的每个重复都存储在数组中,但我真正想做的是将每个重复连接到一个 int 或一个我可以在 main() 中使用的 char。希望它更清楚一点。
  • 所以如果我理解正确的话,你想打印一个double的二进制表示,上面是C/伪代码的混合显示你想要实现什么?
  • 好的,如果我将它编辑到我之前的输出中可能最好,这样就不会再造成混乱了。要点保持不变:在我尝试转换数字的小数部分时,我无法获得可以在我的 main 函数中使用的输出。

标签: c binary


【解决方案1】:

return array[0] ; 只是fra_binary() 中设置的int array[30] 的第一个值。代码丢弃除循环 for (int i = 0 ; i &lt; 30 ; i++) 的第一次计算之外的所有计算。

将其小数部分转换为基数 2

OP 的循环理念是一个很好的起点。然而int array[30] 不足以将所有double 的小数部分编码为“二进制”。

无法找出返回整数的最佳方式

返回 int 是不够的。而是考虑使用字符串 - 或以同样的方式管理整数数组。

使用来自&lt;float.h&gt; 的定义来驱动缓冲区需求。

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

char *fra_binary(char *dest, double x) {
  _Static_assert(FLT_RADIX == 2, "Unexpected FP base");
  double deci;
  double fract = modf(x, &deci);
  fract = fabs(fract);
  char *s = dest;
  do {
    double d;
    fract = modf(fract * 2.0, &d);
    *s++ = "01"[(int) d];
  } while (fract);
  *s = '\0';

  // For debug
  printf("%*.*g --> %.0f and .", DBL_DECIMAL_DIG + 8, DBL_DECIMAL_DIG, x,
      deci);

  return dest;
}

int main(void) {
  // Perhaps           53           - -1021       + 1
  char fraction_string[DBL_MANT_DIG - DBL_MIN_EXP + 1];
  puts(fra_binary(fraction_string, -0.0));
  puts(fra_binary(fraction_string, 1.0));
  puts(fra_binary(fraction_string, asin(-1)));  // machine pi
  puts(fra_binary(fraction_string, -0.1));
  puts(fra_binary(fraction_string, DBL_MAX));
  puts(fra_binary(fraction_string, DBL_MIN));
  puts(fra_binary(fraction_string, DBL_TRUE_MIN));
}

输出

                       -0 --> -0 and .0
                        1 --> 1 and .0
       3.1415926535897931 --> 3 and .001001000011111101101010100010001000010110100011
     -0.10000000000000001 --> -0 and .0001100110011001100110011001100110011001100110011001101
  1.7976931348623157e+308 --> 179769313486231570814527423731704356798070600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 and .0
  2.2250738585072014e-308 --> 0 and .00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
  4.9406564584124654e-324 --> 0 and .000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001

也不清楚为什么输入是long double,但处理是double。建议只使用一种 FP 类型。

【讨论】:

  • 您的 cmets 确实帮助我理解了我遇到的问题。尽管如此,我还是决定完全走另一条路,放弃在单独的函数中处理转换的想法。相反,我处理 main() 函数中的数据并使用 for 循环打印数组。肯定效率较低,但方便。不过,感谢您的所有精彩解释!
【解决方案2】:

请注意,您的算法首先找出分数最高有效位的二进制表示。

将小数部分转换为二进制字符串的一种方法是为函数提供一个字符串和一个字符串长度,并让函数用最多那么多的二进制数字填充它:

/* This function returns the number of chars needed in dst
   to describe the fractional part of value in binary,
   not including the trailing NUL ('\0').
   Returns zero in case of an error (non-finite value).
*/
size_t fractional_bits(char *dst, size_t len, double value)
{
    double  fraction, integral;
    size_t  i = 0;

    if (!isfinite(value))
        return 0;

    if (value > 0.0)
        fraction = modf(value, &integral);
    else
    if (value < 0.0)
        fraction = modf(-value, &integral);
    else {
        /* Zero fraction. */
        if (len > 1) {
            dst[0] = '0';
            dst[1] = '\0';
        } else
        if (len > 0)
            dst[0] = '\0';
        /* One binary digit was needed for exact representation. */
        return 1;            
    }

    while (fraction > 0.0) {
        fraction = fraction * 2.0;
        if (fraction >= 1.0) {
            fraction = fraction - 1.0;
            if (i < len)
                dst[i] = '1';
        } else
            if (i < len)
                dst[i] = '0';
        i++;
    }

    if (i < len)
        dst[i] = '\0';
    else
    if (len > 0)
        dst[len - 1] = '\0';

    return i;
}

上述函数的工作方式与snprintf() 非常相似,只是它只需要double,其小数位将存储为二进制数字字符串(0 或1)。并在发生错误时返回0(非有限double 值)。

另一种选择是使用无符号整数类型来保存位。例如,如果您的代码打算在 double 是 IEEE-754 Binary64 类型或类似类型的架构上工作,则尾数的精度高达 53 位,uint64_t 就足够了。

这是一个例子:

uint64_t  fractional_bits(const double  val, size_t  bits)
{
    double    fraction, integral;
    uint64_t  result = 0;

    if (bits < 1 || bits > 64) {
        errno = EINVAL;
        return 0;
    }

    if (!isfinite(val)) {
        errno = EDOM;
        return 0;
    }

    if (val > 0.0)
        fraction = modf(val, &integral);
    else
    if (val < 0.0)
        fraction = modf(-val, &integral);
    else {
        errno = 0;
        return 0;
    }

    while (bits-->0) {
        result = result << 1;
        fraction = fraction * 2.0;
        if (fraction >= 1.0) {
            fraction = fraction - 1.0;
            result = result + 1;
        }
    }

    errno = 0;
    return result;
}

返回值是小数部分的二进制表示:[i]fractional_part[/i] ≈ [i]result[/i] / 2[sup][i]bits[/i][/sup],其中 [i]bits[/i] 介于 1 和 64 之间,包括 1 和 64。

为了让调用者检测到错误,如果没有发生错误,函数会将errno 清零。如果确实发生错误,则函数返回零,如果值不是有限的,则将errno 设置为EDOM,如果bits 小于1 或大于64,则设置为EINVAL。

如果您实现任意大小的无符号整数类型或位图类型,您可以结合使用这两种方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-29
    • 1970-01-01
    • 1970-01-01
    • 2014-08-27
    • 2016-08-08
    • 2014-10-07
    • 1970-01-01
    相关资源
    最近更新 更多