不同的结果在16位的int机器(MSP430)和32位的int机器(ARM CORTEX)中进行减法运算 [英] Different results in 16 bit int machine(MSP430) and 32 bit int machine(ARM CORTEX) in a subtraction
问题描述
当下面的代码针对像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。但是可以将负数表示为unsigned int。
当-90被提升为unsigned int时,是否意味着数字的符号被忽略?
假设一个数字的符号被忽略。
二进制表示为90是 00000000 01011010
。
当这个被赋值给$ 32 code> s32 这是32位宽的有符号整数变量,
转换如何发生?
为了 s32
等于65446,90需要2的补码。
这将是 00000000 10100110
。
我不了解 s32
成为65446的过程。
在32位宽整数机器中,如ARM, s32
是-90,这是正确的。
要在16位整数机器中修复这种情况,需要 u16c
的类型转换为(int16_t)
。如何解决这个问题?
如IAR Workbench(右下角)所示,添加了 s32
的六进制数据表示)。
显示 s32
成为 0x0000FFA6
。
因此,对于MSP430,机器实现从无符号16位转换为32位,只需预先添加16位。
我的理解是
u16c
将隐式类型提升到unsigned int
。
这取决于 10
( int
的类型的表示形式)。您的理解对于某些系统来说是正确的,我们将首先介绍一下,但正如您将在后面的答案中看到的那样,您将失去图片的很大一部分。
第5.2.4节,环境限制指定类型为 int
的值的范围为-32767到32767;该范围可以由实现自行扩展,但 int
值必须可以表示此范围。
第6.3节.1.3,签名和无符号整数告诉我们来回转换。我不能更好地改写,所以这里是直接引用: 1当整数类型的值转换为另一个整数类型时比_Bool,如果值可以由新的类型表示,它是不变的。 2否则,如果新类型是无符号的,则通过反复添加或减去一个可以在新类型中表示的最大值,直到该值在新类型的范围内。 60) 3否则,新类型被签名,值不能代表它;结果是实现定义的或实现定义的信号被提出。 这一切都支持你的理论, 如果两个操作数的类型相同,否则,如果两个操作数都具有符号整数类型,或者两者都具有无符号整数类型,那么转换次数较小的类型的操作数将被转换为 对于具有更大等级的操作数的类型。 否则,如果具有无符号整数类型的操作数具有等于或大于其他操作数类型的等级,则带有符号整数类型的操作数将转换为具有无符号整数类型的操作数类型。 否则,如果具有有符号整数类型的操作数的类型可以表示具有无符号整数类型的操作数类型的所有值,则将无符号整数类型的操作数转换为操作数的类型带符号整数类型。 否则,两个操作数都将转换为与具有带符号整数类型的操作数类型相对应的无符号整数类型。 所以,从这里可以看出,表达式的类型 数学上10-u16c等于-90。但是可以将负数表示为unsigned int。当-90被提升为unsigned int时,是否意味着一个数字的符号被忽略? 根据附件H.2.2 : C的无符号整数类型在LIA-1意义上是modulo,因为溢出或超出界限的结果将静默地包裹。 换句话说,如果 uint16_t
,但是,如果它存在(它不是需要)有一个范围从0到65535.实现不能扩展;这是一个要求,准确地 [0..65535]
(因此这种类型不需要存在的原因)。 p>
int
如果且仅当 int
值10将被转换为 uint16_t
/ code>是一个16位类型。 但是,第6.3.1.8节,通常的算术应该首先应用 规则,以确定上述三种转换中的哪一种,因为这些规则会更改您在 int
大于16位
10-u16c
可能因系统而异。在 int
是十六位的系统上,该表达式将是一个 uint16_t
。
10
转换为 uint16_t
并且执行减法,结果将是一个大的 uint16_t
。您可以通过使用无符号整数常量(例如 -90U
)看到类似的效果。这在很大程度上得益于来自6.3.1.3之前的引用的规则#2。
当这被分配给32位宽的有符号整数变量的s32时,如何进行转换?
表达式 10-u16c
根据6.3.1.3(上面引用的)中的规则#1转换为 int32_t
值并存储为该值。
要在16位整数机中修复这种情况,需要一个类型转换为
(int16_t)
u16c
。这样做可以解决这个问题吗?
类型转换没有为此讨论添加有用的信息。也许您使用的是非兼容(buggy)编译器。我怀疑手册可能会对此有所了解,但由于我不知道您使用哪个编译器,我无法读取...
When the code below is run against 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 takes 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 32bit wide integer machine like ARM, s32
is -90, which is correct.
To fix this situation in 16bit 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 tounsigned 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)
foru16c
. 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机器(MSP430)和32位的int机器(ARM CORTEX)中进行减法运算的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!