【问题标题】:Converting from short to unsigned short and preserving the bit pattern confusion从 short 转换为 unsigned short 并保留位模式混淆
【发布时间】:2020-05-05 18:35:54
【问题描述】:

我正在做一个项目,我需要获取一系列带符号的 16 位整数、负值和正值,并将它们发送到函数以在单元测试期间进行分析。

由于不同的原因,该函数只接受一个无符号 16 位整数数组,因此我需要将有符号整数存储在一个无符号 16 位整数数组中,并完全保留相同的位模式。我正在使用 gcc (Debian 8.3.0-6) 8.3.0。

unsigned short arr[450];
unsigned short arrIndex = 0;

for (short i = -32768; i < (32767 - 100) ; i = i + 100 )
{
    arr[arrIndex] = i; 

    printf("short value is          : %d\n", i);
    printf("unsigned short value is : %d\n", arr[arrIndex]);
    arrIndex++;
}

即使我告诉 printf 打印带符号的值,我也很惊讶地发现,对于小于零的值,位模式实际上是不同的。前几个值如下:

short value is           : -32768
unsigned short value is  : 32768

short value is           : -32668
unsigned short value is  : 32868

short value is           : -32568
unsigned short value is  : 32968

这里发生了什么,我将如何保留低于零的 i 值的位模式?

【问题讨论】:

  • “有符号的 16 位整数”实际上是指“有符号的 16 位整数”,而您的 int 是 32 位而不是 16 位?
  • @WeatherVane 这就是我的意思,给格式说明符 %d.. 我期待看到负值
  • @WeatherVane 如果我将我的 cmets 改为“shorts”而不是“ints”,你会更开心吗?
  • 您需要明确%d 的期望以及“整数”的含义。最小的int 大小为 16 位。根据通常的整数提升规则,小于原生 int 的可变参数函数的整数参数将被提升为 int。如果您的 int 是 32 位,则它可以处理 signed shortunsigned short 的整个范围。
  • ...编译器不会调整提升以适应格式说明符。 (出于善意,它只会发出关于类型兼容性的警告消息)。

标签: c type-conversion data-conversion unsigned signed


【解决方案1】:

如何将i 的值保留在零以下的位模式?

对于非常常见的 2 的补码编码,以下内容就足够了。

unsigned short us = (unsigned short) some_signed_short;

带有补码和符号大小的 BITD 日,这还不够,代码将使用 unionshortunsigned short

然而,由于负值 2 的补码如何转换为 无符号,位模式被保留用于相同大小的类型。


对于那些小于零的值,位模式实际上是不同的。

位模式相同。它们通过不同的转换路径进行打印,因此具有不同的文本输出。


打印short, unsigned short时,最好使用h printf修饰符。

//printf("short value is          : %d\n", i);
//printf("unsigned short value is : %d\n", arr[arrIndex]);
printf("short value is          : %hd\n", i);
printf("unsigned short value is : %hu\n", arr[arrIndex]);

【讨论】:

  • 但是,这令人困惑。我打印出来的第三个值:短值是:-32568 无符号短值是:32968。如果使用2的补码,-32568不应该变成32568,负值变成正值吗?
  • 没有。通过打印short -32568,该值被转换为int 并打印为-32568。通过打印unsigned short 32968,该值被转换为int 并打印为32968。这里的关键在于打印,作为printf() 的...参数,首先将值转换为int,然后再打印。在您的系统上,int 范围大于 shortunsigned short,因此它保留了原始值。
  • 不应该得到一个数字的 2 的补码,给出相同的数字但符号相反?
  • @Engineer999 请详细解释一下getting是什么意思。
  • @Engineer999 当负的short 值转换为unsigned short 时,算术上会添加USHRT_MAX + 1(根据C 转换规则)并保存总和。这样做的一个副作用是位模式不会改变,因为short 中的符号位是 -32768 位置,unsigned short 中的相同位是 +32768 位置。 USHRT_MAX + 1的变化。
【解决方案2】:

在 C 中,如果您调用可变参数函数并传入任何类型的整数类型,该语言将自动将其提升为相同类型的有符号或无符号 int。然后,当您使用 %d 修饰符将内容打印出来时,您会看到提升的 int 结果。

例如,当你打电话时

printf("short value is          : %d\n", i);

i 的(负)值被提升为具有相同值的signed int,这就是它打印为负的原因。当你然后打电话时

printf("unsigned short value is : %d\n", arr[arrIndex]);

arr[arrIndex] 的(无符号)值被提升为 unsigned int,这就是您看到显示正值的原因。

要解决此问题,请更改您的 printf,以便告诉编译器将结果专门显示为 short 变量:

printf("short value is          : %hd\n", i);
printf("unsigned short value is : %hd\n", arr[arrIndex]);

现在,您会看到值一致。

【讨论】:

  • "arr[arrIndex] 的值被提升为无符号整数" --> 16 位 int/unsigned 为真。 arr[arrIndex] 的值在 32 位上提升为 int
【解决方案3】:

值被正确复制。我们看下面的代码:

#include <stdio.h>

void printit(char *name, short int val)
  {
  printf("%s  %hd  %hu  0x%hX\n", name, val, val, val);
  }

int main()
  {
  short int v1 = 0x8000;
  short int v2 = 0x8064;
  short int v3 = 0x80C8;

  printit("v1", v1);
  printit("v2", v2);
  printit("v3", v3);
  }

在这里,我创建了四个有符号短变量,并将它们设置为位模式。暂时忘记“积极”和“消极”——我只是在这些变量中加入了一些模式。在子程序printit 中,这些值被打印为有符号十进制、无符号十进制和十六进制(以验证它们都是相同的位模式)。现在,看看结果:

v1  -32768  32768  0x8000
v2  -32668  32868  0x8064
v3  -32568  32968  0x80C8

现在您可以看到我只是复制了您使用的值(-32768、-32668 和 -32568)并将它们分配给变量。唯一的区别是我先将它们转换为十六进制。相同的位模式。结果相同。但是,除了在极少数情况下,十进制值为负的位模式的有符号十进制值解释与位模式的无符号十进制解释不同。我建议阅读One's Complement 的二进制数,以及Two's Complement 负二进制数的表示。

【讨论】:

  • Bob short int v1 = 0x8000;实现定义的行为(通常是良性的)开始,因为 0x8000 超出了 short 范围。 short int v1 = -32768;= -0x8000,虽然说明性较差,但没有这个问题。
【解决方案4】:

数据被正确复制,按照您的需要逐位复制。它只是将其显示为有符号值的打印,因为 arr 被声明为无符号值数组。

%d 打印作为ints 传递的数据(按照标准定义?不确定),在普通平台上是 4 字节。传递给printf 的参数在打印之前升级为int,这取决于相关参数是否已签名,是否需要符号扩展。

当打印i,这是一个有符号值时,该值将在打印之前进行符号扩展。例如,如果 i-1(在使用二进制补码的 2 字节有符号值上表示为 0xFFFF),则 i 将升级为 int0xFFFFFFFF(其中也是-1,但用四个字节表示)。

但是,如果i 等于-1,那么在执行arr[arrIndex] = i 时,arr[arrIndex] 确实会设置为0xFFFF,按照您的需要逐位复制。但是,由于arr[arrIndex] 是未签名的,因此在未签名的世界中,0xFFFF 代表65535。然后,当打印arr[arrIndex] 时,由于arr[arrIndex] 是无符号的,该值将进行符号扩展,因为它是一个无符号值。因此,0xFFFF 将升级为 0x0000FFFF,等于 65535,并按原样打印。

我们可以通过强制arr 在打印之前被视为已签名来验证这一点。这样,arr 的处理方式将与 i 的处理方式相同。

#include <stdio.h>
int main() {
    unsigned short arr[450];
    unsigned short arrIndex = 0;

    for (signed short i = -32768; i < (32767 - 100) ; i = i + 100 )
    {
        arr[arrIndex] = i;
        printf("short value is          : %d\n", i);
        printf("unsigned short value is : %d\n", ((signed short*)arr)[arrIndex]);
        arrIndex++;
    }
}

输出:

short value is          : -32768
unsigned short value is : -32768
short value is          : -32668
unsigned short value is : -32668
short value is          : -32568
unsigned short value is : -32568
short value is          : -32468
unsigned short value is : -32468
short value is          : -32368
unsigned short value is : -32368
short value is          : -32268
unsigned short value is : -32268
short value is          : -32168
unsigned short value is : -32168

或者,我们可以直接将arr 声明为带符号值的数组来达到相同的结果:

#include <stdio.h>
int main() {
    signed short arr[450];
    unsigned short arrIndex = 0;

    for (signed short i = -32768; i < (32767 - 100) ; i = i + 100 )
    {
        arr[arrIndex] = i;
        printf("short value is          : %d\n", i);
        printf("unsigned short value is : %d\n", arr[arrIndex]);
        arrIndex++;
    }
}

【讨论】:

    【解决方案5】:

    请检查for 循环限制,就好像你从-32768&lt;(32767-100)100 个值的跳跃中填充655 个数组元素,而您只声明了 450

    此外,要打印unsigned short 值,您需要使用%u(或等效的%hu,因为shorts 转换为int 以供printf() 使用)格式说明符。

    使用这个例子:

    #include <stdio.h>
    
    int main()
    {
            short i;
            for (i = -32768; i < (32767 - 100); i += 100) {
                    unsigned short j = i;
                    printf("Signed  : %d\n", i);
                    printf("Unsigned: %u\n", j);
            }
    
            return 0;
    }
    

    它将产生:

    $ a.out
    Signed  : -32768
    Unsigned: 32768
    Signed  : -32668
    Unsigned: 32868
    Signed  : -32568
    Unsigned: 32968
    Signed  : -32468
    ...
    Signed  : -268
    Unsigned: 65268
    Signed  : -168
    Unsigned: 65368
    Signed  : -68
    Unsigned: 65468
    Signed  : 32
    Unsigned: 32
    Signed  : 132
    Unsigned: 132
    ...
    Signed  : 32432
    Unsigned: 32432
    Signed  : 32532
    Unsigned: 32532
    Signed  : 32632
    Unsigned: 32632
    $ _
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-11
      • 2015-01-20
      • 1970-01-01
      相关资源
      最近更新 更多