【问题标题】:Converting a negative decimal into binary in C在C中将负十进制转换为二进制
【发布时间】:2019-04-26 00:51:45
【问题描述】:

我目前正在开发一个程序,该程序(除其他外)必须将十进制数转换为二进制、八进制和十六进制。 这已经适用于此代码:

    int e = 0;
    }
        while(i != 0){
            str[e] = (i%b) + '0';
            i = i / b;
            if(str[e] > '9'){
                str[e] = str[e] + 7;
            }
            e++;

    }
    if(vorzeichen == -1){
        str[e] = '1';
        e++;
    }
    if(b == 16){
        str[e] = 'x';
        str[e+1] = '0';
    }
    else if(b == 8){
        str[e] = '0';
    }
}

b 是基数(二进制为 2,八进制为 8,六进制为 16),i 是我要转换的数字。 这给出了一串字符,然后我将其反转以获得正确的数字。现在,如果我用负数尝试这个,它不仅会给出包含 0 和 1 的字符串,还会给出 /,即 ASCII 表上的 '0' -1。对于八进制和十进制,它还会给出 ASCII 表中“/”下方的字符。我尝试了不同的可能解决方案,但似乎都没有给出预期的结果。我在互联网上看到的是我必须使用 2s Complement 我一直在尝试使用它。它似乎对我不起作用。

【问题讨论】:

  • snprintf(str, size_of_str, "%o", integer_value)?将 "%o" 替换为 "%x" 以表示十六进制。对于二进制,我建议使用移位和 AND 等按位运算。
  • 我不能使用库函数
  • 那我建议你花点时间learn how to debug your code。
  • 在这种情况下,我建议先在 paper 上编写算法,然后看看它在不同输入下的表现如何。一旦你有了可靠的东西,就很容易用代码编写它。花点时间学习。

标签: c binary decimal


【解决方案1】:

如果您想显示负小数,您只需将您的 int 转换为 unsigned int,如下所示:

unsigned int value = (unsigned int)i;

现在您只需在程序中使用value 而不是i 就可以了。 这是一个很好的解释:Converting negative decimal to binary

【讨论】:

    【解决方案2】:

    在不同基数/基数之间转换时,始终使用无符号整数类型。

    假设您有想要转换的long num。使用unsigned long u。要以二进制补码格式表示负值,您可以使用

    if (num < 0)
        u = 1 + (~(unsigned long)(-num));
    else
        u = num;
    

    甚至更短,

    unsigned long  u = (num < 0) ? 1 + (~(unsigned long)(-num)) : num;
    

    这适用于所有架构(num == LONG_MIN 除外,在这种情况下,上述情况在技术上是未定义的行为),即使是那些内部不使用补码的架构,因为我们基本上将 num 的绝对值。如果num 最初是负数,我们就对无符号值进行二进制补码。


    在评论中,chux 提出了一种不依赖 UB 的替代形式 num == LONG_MIN(除非 LONG_MAX == ULONG_MAX,这将是非常奇怪的事情):

    unsigned long  u = (num < 0) ? 1 + (~((unsigned long)(-1 - num) + 1)) : num;
    

    这可能看起来“丑陋”,但一个理智的 C 编译器应该能够完全优化其中任何一个,而不是在具有二进制补码整数的体系结构上。 chux 的版本通过从-1 中减去负数num 来避免未定义的行为,从而将-1 映射到0、-2 到1 等等,确保所有负值都可以表示为非负@ 987654337@。然后将该值转换为unsigned long。这会增加 1,以说明较早的 -1。此过程产生num 的正确否定。

    也就是说,要获得long的绝对值,可以使用

    unsigned long  abs_long(const long  num)
    {
        return (num < 0) ? (unsigned long)(-1 - num) + 1u : (unsigned long)num;
    }
    

    【讨论】:

    • 不依赖内部补码的好方法。 Corner oops:“它是带有 2 的补码 i == INT_MIN 和 -num 的 UB。
    • @chux:我确定您的意思是LONG_MIN,因为我使用的是long,而不是int。我不确定是否值得在这里避免未定义的行为,也不确定如何在没有更大整数类型的情况下做到这一点。有什么建议吗?
    • 是LONG_MIN 的意思,而不是INT_MIN。为了避免更大的类型,1 + (~((unsigned long)(-1 - num) + 1));(正确,但丑陋)或其他代码。
    • @chux:C 标准中有什么规定LONG_MIN+LONG_MAX 不能小于-1 或大于0?如果不是,LONG_MIN 并不是唯一需要特别注意的情况。
    • 是的,“LONG_MIN+LONG_MAX 不能小于 -1 或大于 0”是 C11 §6.2.6.2 的结果 2. 在迂腐的注释中,LONG_MAX == ULONG_MAX 是可能的但没有几十年来遇到这样的机器。 IMO,OP 的目标缺乏高度便携性解决方案的明确性 - 但可以拥有足够好的解决方案。
    【解决方案3】:

    % 是余数函数,而不是 mod。

    使用b==2,i%b 返回 [-1, 0, 1]。这不是str[e] = (i%b) + '0'; 所需的功能请参阅... difference between “mod” and “remainder”

    这就是'/' 的原因,并且“也给出了'/'下面的字符”。


    从“右”开始构建字符串

    使用 2 的补码 int,一个简单的方法是转换为 unsigned 并避免来自 % 的否定结果。由于代码使用% 来提取最低有效数字,因此从右到左遍历缓冲区。

    #include <limits.h>
    
    ...
    unsigned u = i;
    
    // make a temporary buffer large enough for any string output in binary
    //           v------v Size of `u` in "bytes"
    //           |      |   v------v Size of a "byte" - commonly 8
    char my_buff[sizeof u & CHAR_BIT + 1];
    int e = 0;
    
    // Form a pointer to the end so code assigns the least significant digits on the right
    char *p = &my_buff[sizeof my_buff - 1];
    
    // Strings are null character terminated
    *p = '\0';
    
    // Use a `do` loop to insure at least one pass. Useful when `i==0` --> "0"
    do {
      p--; 
      p[e] = "0123456789ABCDEF"[u%b];  // Select desired digit
      u = u / b;
    } while (u);
    
    // "prepend" characters as desired
    if(b == 16){
      *(--p) = 'x';
      *(--p) = '0';
    }
    else if(b == 8 && i != 0){
      *(--p) = '0';
    }
    
    strcpy(str, p);
    

    【讨论】:

      猜你喜欢
      • 2021-05-16
      • 1970-01-01
      • 1970-01-01
      • 2012-12-26
      • 2021-12-07
      • 1970-01-01
      • 2019-03-24
      • 1970-01-01
      相关资源
      最近更新 更多