16位int机器和32位int机器在减法中的结果不同 [英] Different results between a 16-bit int machine and a 32-bit int machine in a subtraction

查看:337
本文介绍了16位int机器和32位int机器在减法中的结果不同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当下面的代码在MSP430微控制器等16位整数机器上运行时,s32产生65446

#include <stdint.h>

uint16_t   u16c;
int32_t    s32;

int main()
{
    u16c = 100U;
    s32  = 10 - u16c;
}

我的理解是10 - u16c可以将隐式类型提升为unsigned int.数学上10 - u16c等于-90.但是怎么可能将一个负数表示为一个无符号的int?

当-90提升为unsigned int时,是否表示数字的符号会被忽略?

假设,数字的符号被忽略.
90的二进制表示形式是00000000 01011010. 当将此值分配给32位宽的带符号整数变量s32时, 转型如何发生?

为了使s32等于65446,90必须取2的补码. 那将是00000000 10100110.

我不了解s32成为65446的过程.

在32位宽的整数计算机(如ARM CORTEX)中,s32为-90,这是正确的.

要解决此问题,请在16位整数计算机中为u16c键入(int16_t).如何解决这个问题?

添加了s32的十六进制数据表示形式,如IAR Workbench(右下角)所示. 显示s32变为0x0000FFA6. 因此,对于MSP430,从无符号16位转换为有符号32位的机器实现,只需添加16 0的位即可.

解决方案

我的理解是10-u16c隐式地提升为unsigned int.

这取决于10(实际上是int)类型的表示形式.您的理解对某些系统是正确的,我们将首先介绍,但是正如您稍后将在此答案中看到的那样,您会丢失大部分图片.

第5.2.4节,环境限制指定int类型的值的范围可以从-32767到32767;可以根据实现情况酌情扩展此范围,但是int必须能够表示该范围.

uint16_t,但是,(如果存在)(不是必需)的范围是0到65535.必须精确地将范围设置为 [0..65535](因此不需要这种类型的原因).

第6.3.1.3节,有符号和无符号整数告诉我们来回的转换.我无法更好地解释它,所以这里有一个直接引号:

1将整数类型的值转换为_Bool以外的其他整数类型时,如果该值可以用新类型表示,则该值不变.

2否则,如果新类型是无符号的,则通过重复添加或减去比新类型可表示的最大值多一个值来转换该值,直到该值在新类型的范围内. > 60)

3否则,将对新类型进行签名,并且无法在其中表示值;结果是实现定义的,还是引发实现定义的信号.

这一切都支持您的理论,即当且仅当 int是十六位类型时,int值10才能转换为uint16_t . 但是,第6.3.1.8节,常规算术应该先应用转化 规则,以决定发生上述三个转化中的哪一个,因为这些规则会改变您在int为大于十六位:

如果两个操作数具有相同的类型,则无需进一步转换.

否则,如果两个操作数都具有符号整数类型或都具有无符号整数类型,则将具有较小整数转换等级的类型的操作数转换为具有较高等级的操作数的类型.

否则,如果具有无符号整数类型的操作数的秩大于或等于另一个操作数的类型的秩,则具有符号整数类型的操作数将转换为具有无符号整数类型的操作数的类型./p>

否则,如果带符号整数类型的操作数的类型可以表示带无符号整数类型的操作数的所有值,则将带无符号整数类型的操作数转换为带符号整数的操作数的类型类型.

否则,两个操作数都将转换为与带符号整数类型的操作数类型相对应的无符号整数类型.


因此,如您所见,表达式10-u16c的类型可能因系统而异.在int是16位的系统上,该表达式将是uint16_t.

数学上10-u16c等于-90.但是如何将一个负数表示为一个无符号的int.当-90提升为unsigned int时,是否表示数字的符号会被忽略?

根据附件H.2.2 :

C的无符号整数类型在LIA-1意义上是模"的,因为溢出或越界结果会自动换行.

换句话说,如果将10转换为uint16_t并执行减法,则结果将是一个 large 数字,在这种情况下,您可以通过显式转换来看到该数字两个操作数(即将它们强制转换)都为uint16_t.通过使用无符号整数常量(例如-90U),您可能会看到类似的效果. 6.3.1.3之前的报价中的规则2在很大程度上支持了这一点.


将其分配给32位带符号整数变量s32时,如何进行转换?

表达式10-u16c根据6.3.1.3中的规则#1(上面引用)转换为int32_t值,并存储为该值.


要在16位整数计算机中解决此问题,需要为u16c键入(int16_t)的类型.如何解决这个问题?

类型转换未在此讨论中添加任何有用的信息.也许您使用的是不兼容的(buggy)编译器.我怀疑手册可能对此有所了解,但是由于我不知道您使用的是哪个编译器,所以我看不懂...

When the code below is run against a 16-bit integer machine like MSP430 micro controller, s32 yields 65446

#include <stdint.h>

uint16_t   u16c;
int32_t    s32;

int main()
{
    u16c = 100U;
    s32  = 10 - u16c;
}

My understanding is that 10 - u16c gets implicit type promotion to unsigned int. Mathematically 10 - u16c equals to -90. But how is it possible to represent a negative number as an unsigned int?

When -90 gets promoted to unsigned int, does it mean that the sign of a number is ignored?

Lets suppose, the sign of a number is ignored.
The binary representation of 90 is 00000000 01011010. When this gets assigned to s32 which is 32-bit wide signed integer variable, how does the transformation take place?

In order for s32 equal to 65446, 90 has to take 2's complement. That would be 00000000 10100110.

I am not confident in understand the process of s32 becoming 65446.

In a 32-bit wide integer machine like ARM CORTEX, s32 is -90, which is correct.

To fix this situation in 16-bit integer machine, there needs a typecast of (int16_t) for u16c. How does this remedy this problem?

Added hexa data representation of s32 as shown from IAR Workbench (Lower right corner). It is shown that s32 becomes 0x0000FFA6. So for MSP430, the machine implementation of converting from unsigned 16 bit to signed 32 bit, it simply prepends 16 0's bits.

解决方案

My understanding is that 10-u16c gets implicit type promotion to unsigned int.

This depends upon the representation of the type of 10 (int, as it were). Your understanding is correct for some systems, and we'll cover that first, but as you'll see later on in this answer, you're missing a big part of the picture.

Section 5.2.4, Environmental limits specifies that values of type int can range from -32767 to 32767; this range may be extended at the discretion of implementations, but int values must be able to represent this range.

uint16_t, however, if it exists (it's not required to) has a range from 0 to 65535. Implementations can't extend that; it's a requirement that the range be precisely [0..65535] (hence the reason this type isn't required to exist).

Section 6.3.1.3, Signed and unsigned integers tells us about the conversions to and fro. I couldn't paraphrase it better, so here's a direct quote:

1 When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.

2 Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.60)

3 Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

This all supports your theory that the int value 10 would get converted to a uint16_t if and only if int is a sixteen bit type. However, section 6.3.1.8, usual arithmetic conversion rules should be applied first to decide which of the three above conversions takes place, as these rules change the way you'll look at the conversion when int is greater than sixteen bits:

If both operands have the same type, then no further conversion is needed.

Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.

Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.

Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.

Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.


So, as you can see from this, the type of the expression 10-u16c might vary from system to system. On systems where int is sixteen bits, that expression will be a uint16_t.

Mathematically 10-u16c equals to -90. But how is it possible to represent a negative number as an unsigned int. When -90 gets promoted to unsigned int, does it mean that the sign of a number is ignored?

According to Annex H.2.2:

C's unsigned integer types are ''modulo'' in the LIA-1 sense in that overflows or out-of-bounds results silently wrap.

In other words, if 10 gets converted to a uint16_t and the subtraction is performed, the result will be a large number, in this case you can see that number by explicitly converting both operands (i.e. casting them) to a uint16_t. You could see a similar effect by using unsigned integer constants such as -90U. This is largely supported by rule #2 from the quote from 6.3.1.3 earlier.


When this gets assigned to s32 which is 32 bit wide signed integer variable, how does the transformation takes place?

The expression 10-u16c is converted according to rule #1 in 6.3.1.3 (quoted above) to an int32_t value and stored as that value.


To fix this situation in 16bit integer machine, there needs a typecast of (int16_t) for u16c. How does this remedy this problem?

The typecast adds no useful information to this discussion. Perhaps you're using a non-compliant (buggy) compiler. I suspect the manual might shed some light on this, but since I don't know which compiler you're using I can't read it...

这篇关于16位int机器和32位int机器在减法中的结果不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆