【问题标题】:Manually cast signed char手动转换签名字符
【发布时间】:2011-09-25 14:14:09
【问题描述】:

我正在使用一些嵌入式硬件,即 Rabbit SBC,它使用 Dynamic C 9。

我正在使用微控制器通过其中一个串行端口从数字罗盘传感器读取信息。

传感器使用单个有符号字节将值发送到微控制器。 (-85 到 85)

当我收到这些数据时,我会将其放入 char 变量中

这适用于正值,但是当传感器开始发送负值时,读数会跳到 255,然后又回到 0。我想这是因为最后一位用于确定负值/positive,并且正在扭曲真实值。

我最初的想法是将我的数据类型更改为signed char

但是,我遇到的问题是,我使用的微控制器上的 Dynamic C 版本本身不支持带符号的字符值,只支持无符号的。

我想知道是否有办法将我收到的数据手动转换为有符号值?

【问题讨论】:

  • 有符号和无符号值都只是一堆位,是您的解释使它们有符号或无符号。例如,如果您的硬件产生 2 的补码,如果您读取 0xff,您可以将其解释为 -1 或 255,但它们实际上是相同的数字。
  • @Shahbaz:这不是真的。符号位会影响移位操作,以及任何转换为​​更大类型的操作。
  • 你不能把它转换成int(假设这个编译器有一个签名的int)。这个平台有 gcc 端口吗?
  • 所以你有一个非 C 语言的编译器,但看起来像它。您获得的任何 C 答案都可能对您的编译器无效。有没有办法让你的编译器成为 C 编译器?还是让您获得另一个编译器?
  • @Shahbaz:你敢打赌它会影响变速!如果它是负数并且你右移,它会将符号位保持在正确的位置。

标签: c dynamic-c


【解决方案1】:

你只需要拿出你的参考书,看看你的控制器是如何表示负数的。剩下的就是打字了。

例如二进制补码取值取模256表示,所以只需要通过模数调整即可。

int signed_from_unsignedchar(unsigned char c)
{
    int result = c;
    if (result >= 128) result -= 256;
    return result;
}

One's complement 更简单:只需翻转位即可。

int signed_from_unsignedchar(unsigned char c)
{
    int result = c;
    if (result >= 128) result = -(int)(unsigned char)~c;
    return result;
}

Sign-magnitude通过设置高位表示负数,所以只需清除该位并取反即可:

int signed_from_unsignedchar(unsigned char c)
{
    int result = c;
    if (result >= 128) result = -(result & 0x7F);
    return result;
}

【讨论】:

  • result -= 256 并不意味着否定。事实上它对result 没有影响。 result = 256-result 就是你想要的
  • @Shahbaz:他确实想要否定任何事情。当他的编译器只愿意发出一个未签名的扩展时,他想获得签名扩展。这可以通过减去较小类型的模数来正确完成。
  • 我相信一个人的补码大小写不正确。在这种情况下低字节已经是正确的,因此您只需翻转高字节,即result = c ^ ~0xff。请注意,这将适用于一个补码和二进制补码。并不是说它太重要了;现在我真的不希望看到任何芯片使用反码,因为二进制补码更容易。
  • 啊,是的,result 的类型是 int。我的坏
  • @Jan True,但它假定主机设备和控制器都是互补的。 (也许这是一个安全的假设。)
【解决方案2】:

我认为这就是您所追求的(假设是 32 位 int 和 8 位 char):

unsigned char c = 255;
int i = ((int)(((unsigned int)c) << 24)) >> 24;

当然我在这里假设您的平台确实支持有符号整数,但事实可能并非如此。

【讨论】:

  • 这也假设了一个二进制补码表示。
  • 您在这里对int 的大小做出了假设,这几乎可以肯定是没有根据的。如果他使用的微控制器甚至没有提供完整的 c 编译器,那么字长可能小于 32 位,甚至可能不是 8 bis 的倍数。
  • @dmckee:我在第一句话中说这是一个假设。它给出了一个粗略的想法,仅此而已。
【解决方案3】:

有符号和无符号值都只是一堆位,是您的解释使它们有符号或无符号。例如,如果您的硬件产生 2 的补码,如果您读取 0xff,您可以将其解释为 -1 或 255,但它们实际上是相同的数字。

现在,如果您只有 unsigned char 可供使用,您必须用它来模拟负值的行为。

例如:

c < 0

更改为

c > 127

幸运的是,添加不需要更改。减法也是一样的(检查一下,我不是 100% 确定)。

例如,对于乘法,您需要自己检查。首先,在 2 的补码中,您可以通过以下方式获得数字的正值:

pos_c = ~neg_c+1

从数学上讲,256-neg_c 模 256 的全等就是 -neg_c

现在假设您想将两个无符号数相乘,但您想将它们解释为有符号数。

unsigned char abs_a = a, abs_b = b;
char final_sign = 0; // 0 for positive, 1 for negative
if (a > 128)
{
    abs_a = ~a+1
    final_sign = 1-final_sign;
}
if (b > 128)
{
    abs_b = ~b+1
    final_sign = 1-final_sign;
}
result = abs_a*abs_b;
if (sign == 1)
    result = ~result+1;

你懂的!

【讨论】:

    【解决方案4】:

    如果您的平台支持signed ints,请查看其他一些答案。

    如果不是,并且该值肯定在 -85 和 +85 之间,并且它是二进制补码,则将 85 添加到输入值并制定程序逻辑来解释 0 和 170 之间的值,这样您就不必有符号整数了。

    如果是补码,试试这个:

    if (x >= 128) {
        x = 85 - (x ^ 0xff);
    } else {
        x = x + 85;
    }
    

    这也会给你留下一个介于 0 和 170 之间的值。

    编辑:是的,还有符号大小。然后在此处使用相同的代码,但将第二行更改为x = 85 - (x &amp; 0x7f)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-06
      • 2011-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多