c++ 在减法期间转换为字节 (unit8_t) 不会像我预期的那样强制下溢;输出是 int16_t;为什么?
c++ casting to byte (unit8_t) during subtraction won't force underflow like I expect; output is int16_t; why?
注意byte
是8位类型(uint8_t),unsigned int是16位类型(uint16_t)。
以下未产生我期望的结果。我希望它下溢并且结果总是 uint8_t,但它变成了 signed int (int16_t) 而不是!!!为什么?
特别关注以下代码行:(byte)seconds - tStart
我希望它的输出始终是 unsigned 8 位值 (uint8_t),但它输出的是 signed 16-位值:int16_t.
如何使减法的结果始终为 uint8_t 类型?
while (true)
{
static byte tStart = 0;
static unsigned int seconds = 0;
seconds++;
//Print output from microcontroller
typeNum((byte)seconds); typeString(", "); typeNum(tStart); typeString(", ");
typeNum((byte)seconds - tStart); typeString("\n");
if ((byte)seconds - tStart >= (byte)15)
{
typeString("TRUE!\n");
tStart = seconds; //update
}
}
示例输出:
第 1 列 是 (byte)seconds
,第 2 列 是 tStart
,第 3 列 是第 1 列减去第 2 列 ((byte)seconds - tStart
)
请注意,一旦第 1 列从 255 溢出到 0,第 3 列变为负数 (int8_t)。我希望(并希望)它通过下溢保持正(无符号)8 位值。
196, 195, 1
197, 195, 2
198, 195, 3
199, 195, 4
200, 195, 5
201, 195, 6
202, 195, 7
203, 195, 8
204, 195, 9
205, 195, 10
206, 195, 11
207, 195, 12
208, 195, 13
209, 195, 14
210, 195, 15
TRUE!
211, 210, 1
212, 210, 2
213, 210, 3
214, 210, 4
215, 210, 5
216, 210, 6
217, 210, 7
218, 210, 8
219, 210, 9
220, 210, 10
221, 210, 11
222, 210, 12
223, 210, 13
224, 210, 14
225, 210, 15
TRUE!
226, 225, 1
227, 225, 2
228, 225, 3
229, 225, 4
230, 225, 5
231, 225, 6
232, 225, 7
233, 225, 8
234, 225, 9
235, 225, 10
236, 225, 11
237, 225, 12
238, 225, 13
239, 225, 14
240, 225, 15
TRUE!
241, 240, 1
242, 240, 2
243, 240, 3
244, 240, 4
245, 240, 5
246, 240, 6
247, 240, 7
248, 240, 8
249, 240, 9
250, 240, 10
251, 240, 11
252, 240, 12
253, 240, 13
254, 240, 14
255, 240, 15
TRUE!
0, 255, -255
1, 255, -254
2, 255, -253
3, 255, -252
4, 255, -251
5, 255, -250
6, 255, -249
7, 255, -248
8, 255, -247
9, 255, -246
10, 255, -245
11, 255, -244
12, 255, -243
13, 255, -242
14, 255, -241
15, 255, -240
16, 255, -239
17, 255, -238
18, 255, -237
19, 255, -236
20, 255, -235
21, 255, -234
22, 255, -233
23, 255, -232
24, 255, -231
25, 255, -230
26, 255, -229
27, 255, -228
28, 255, -227
29, 255, -226
30, 255, -225
31, 255, -224
32, 255, -223
33, 255, -222
34, 255, -221
35, 255, -220
这是上面的 typeNum
函数:
//--------------------------------------------------------------------------------------------
//typeNum (overloaded)
//-see AVRLibC int to string functions: http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html
//--------------------------------------------------------------------------------------------
//UNSIGNED:
void typeNum(uint8_t myNum)
{
char buffer[4]; //3 for the number (up to 2^8 - 1, or 255 max), plus 1 char for the null terminator
utoa(myNum, buffer, 10); //base 10 number system
typeString(buffer);
}
void typeNum(uint16_t myNum)
{
char buffer[6]; //5 for the number (up to 2^16 - 1, or 65535 max), plus 1 char for the null terminator
utoa(myNum, buffer, 10); //base 10 number system
typeString(buffer);
}
void typeNum(uint32_t myNum)
{
char buffer[11]; //10 chars for the number (up to 2^32 - 1, or 4294967295 max), plus 1 char for the null terminator
ultoa(myNum, buffer, 10); //base 10 number system
typeString(buffer);
}
//SIGNED:
void typeNum(int8_t myNum)
{
char buffer[5]; //4 for the number (down to -128), plus 1 char for the null terminator
itoa(myNum, buffer, 10); //base 10 number system
typeString(buffer);
}
void typeNum(int16_t myNum)
{
char buffer[7]; //6 for the number (down to -32768), plus 1 char for the null terminator
itoa(myNum, buffer, 10); //base 10 number system
typeString(buffer);
}
void typeNum(int32_t myNum)
{
char buffer[12]; //11 chars for the number (down to -2147483648), plus 1 char for the null terminator
ltoa(myNum, buffer, 10); //base 10 number system
typeString(buffer);
}
所以我想通了:
答案很简单,但背后的理解却不是。
答案:
(如何修复):
不要使用 (byte)seconds - tStart
,而是使用 (byte)((byte)seconds - tStart)
。而已!问题解决了!您需要做的就是将数学运算(在本例中为减法)的 output 也转换为 byte
,并且它是固定的!否则它 returns 作为 signed int,这会产生错误行为。
那么,为什么会这样呢?
答案:
在 C、C++ 和 C# 中,不存在针对字节的数学运算! 显然,+、- 等运算符所需的汇编级函数不存在对于字节输入。相反,在进行操作之前,首先将所有字节隐式转换(提升)为 int,然后对 ints 进行数学运算,完成后,它 return也是一个int!
因此,此代码 (byte)seconds - tStart
由编译器隐式转换(在本例中提升)如下:(int)(byte)seconds - (int)tStart
...它 return 是一个 int
也。令人困惑,是吗?我当然这么认为!
这里有一些关于这个问题的更多阅读:
(星号、*越多越有用)
- *****byte + byte = int... why? <--特别有用
- *****Implicit type conversion rules in C++ operators <--ESPECIALLY USEFUL. This answer here 显示何时发生隐式强制转换,并指出,“注意。操作的最小大小为
int
。因此 short
/char
在操作完成之前被提升为 int
。"
- *****Google search for "c++ does implicit casting occur with comparisons?"
- https://www.tutorialspoint.com/cprogramming/c_type_casting.htm
- http://www.improgrammer.net/type-casting-c-language/
- Google search for "c++ implicit casting"
- http://www.cplusplus.com/doc/tutorial/typecasting/
- http://en.cppreference.com/w/cpp/language/implicit_conversion
- http://en.cppreference.com/w/cpp/language/operator_comparison
现在让我们看一些真实的 C++ 示例:
这是一个完整的 C++ 程序,您可以编译和 运行 测试表达式以查看 return 类型是什么,以及它是否已被编译器隐式转换为您不知道的类型'我打算:
#include <iostream>
using namespace std;
//----------------------------------------------------------------
//printTypeAndVal (overloaded function)
//----------------------------------------------------------------
//UNSIGNED:
void printTypeAndVal(uint8_t myVal)
{
cout << "uint8_t = " << (int)myVal << endl; //(int) cast is required to prevent myVal from printing as a char
}
void printTypeAndVal(uint16_t myVal)
{
cout << "uint16_t = " << myVal << endl;
}
void printTypeAndVal(uint32_t myVal)
{
cout << "uint32_t = " << myVal << endl;
}
void printTypeAndVal(uint64_t myVal)
{
cout << "uint64_t = " << myVal << endl;
}
//SIGNED:
void printTypeAndVal(int8_t myVal)
{
cout << "int8_t = " << (int)myVal << endl; //(int) cast is required to prevent myVal from printing as a char
}
void printTypeAndVal(int16_t myVal)
{
cout << "int16_t = " << myVal << endl;
}
void printTypeAndVal(int32_t myVal)
{
cout << "int32_t = " << myVal << endl;
}
void printTypeAndVal(int64_t myVal)
{
cout << "int64_t = " << myVal << endl;
}
//FLOATING TYPES:
void printTypeAndVal(float myVal)
{
cout << "float = " << myVal << endl;
}
void printTypeAndVal(double myVal)
{
cout << "double = " << myVal << endl;
}
void printTypeAndVal(long double myVal)
{
cout << "long double = " << myVal << endl;
}
//----------------------------------------------------------------
//main
//----------------------------------------------------------------
int main()
{
cout << "Begin\n\n";
//Test variables
uint8_t u1 = 0;
uint8_t u2 = 1;
//Test cases:
//for a single byte, explicit cast of the OUTPUT from the mathematical operation is required to get desired *unsigned* output
cout << "uint8_t - uint8_t:" << endl;
printTypeAndVal(u1 - u2); //-1 (bad)
printTypeAndVal((uint8_t)u1 - (uint8_t)u2); //-1 (bad)
printTypeAndVal((uint8_t)(u1 - u2)); //255 (fixed!)
printTypeAndVal((uint8_t)((uint8_t)u1 - (uint8_t)u2)); //255 (fixed!)
cout << endl;
//for unsigned 2-byte types, explicit casting of the OUTPUT is required too to get desired *unsigned* output
cout << "uint16_t - uint16_t:" << endl;
uint16_t u3 = 0;
uint16_t u4 = 1;
printTypeAndVal(u3 - u4); //-1 (bad)
printTypeAndVal((uint16_t)(u3 - u4)); //65535 (fixed!)
cout << endl;
//for larger standard unsigned types, explicit casting of the OUTPUT is ***NOT*** required to get desired *unsigned* output! IN THIS CASE, NO IMPLICIT PROMOTION (CAST) TO A LARGER *SIGNED* TYPE OCCURS.
cout << "unsigned int - unsigned int:" << endl;
unsigned int u5 = 0;
unsigned int u6 = 1;
printTypeAndVal(u5 - u6); //4294967295 (good--no fixing is required)
printTypeAndVal((unsigned int)(u5 - u6)); //4294967295 (good--no fixing was required)
cout << endl;
return 0;
}
您也可以在此处在线 运行 此程序:http://cpp.sh/6kjgq
这是输出。请注意,单无符号字节 uint8_t - uint8_t
和双无符号字节 uint16_t - uint16_t
都隐式转换(被 C++ 编译器提升)为 4 字节 signed int32_t
(int
) 变量类型。这是您需要注意的行为。因此,这些减法的结果是 negative,这是最初让我感到困惑的异常行为,因为我曾预料到它会下溢成为无符号变量的最大值(因为我们是做 0 - 1)。 为了实现所需的下溢,我不得不显式将减法的输出结果转换为所需的无符号类型,而不仅仅是 inputs。 但是,对于 unsigned int
的情况,不需要对结果进行显式转换。
Begin
uint8_t - uint8_t:
int32_t = -1
int32_t = -1
uint8_t = 255
uint8_t = 255
uint16_t - uint16_t:
int32_t = -1
uint16_t = 65535
unsigned int - unsigned int:
uint32_t = 4294967295
uint32_t = 4294967295
这是另一个简短的程序示例,显示单个无符号字节 (unsigned char
) 变量在操作时被提升为有符号整数 (int
)。
#include <stdio.h>
int main(int argc, char **argv)
{
unsigned char x = 130;
unsigned char y = 130;
unsigned char z = x + y;
printf("%u\n", x + y); // Prints 260.
printf("%u\n", z); // Prints 4.
}
输出:
260
4
在这里测试:http://cpp.sh/84eo
注意byte
是8位类型(uint8_t),unsigned int是16位类型(uint16_t)。
以下未产生我期望的结果。我希望它下溢并且结果总是 uint8_t,但它变成了 signed int (int16_t) 而不是!!!为什么?
特别关注以下代码行:(byte)seconds - tStart
我希望它的输出始终是 unsigned 8 位值 (uint8_t),但它输出的是 signed 16-位值:int16_t.
如何使减法的结果始终为 uint8_t 类型?
while (true)
{
static byte tStart = 0;
static unsigned int seconds = 0;
seconds++;
//Print output from microcontroller
typeNum((byte)seconds); typeString(", "); typeNum(tStart); typeString(", ");
typeNum((byte)seconds - tStart); typeString("\n");
if ((byte)seconds - tStart >= (byte)15)
{
typeString("TRUE!\n");
tStart = seconds; //update
}
}
示例输出:
第 1 列 是 (byte)seconds
,第 2 列 是 tStart
,第 3 列 是第 1 列减去第 2 列 ((byte)seconds - tStart
)
请注意,一旦第 1 列从 255 溢出到 0,第 3 列变为负数 (int8_t)。我希望(并希望)它通过下溢保持正(无符号)8 位值。
196, 195, 1
197, 195, 2
198, 195, 3
199, 195, 4
200, 195, 5
201, 195, 6
202, 195, 7
203, 195, 8
204, 195, 9
205, 195, 10
206, 195, 11
207, 195, 12
208, 195, 13
209, 195, 14
210, 195, 15
TRUE!
211, 210, 1
212, 210, 2
213, 210, 3
214, 210, 4
215, 210, 5
216, 210, 6
217, 210, 7
218, 210, 8
219, 210, 9
220, 210, 10
221, 210, 11
222, 210, 12
223, 210, 13
224, 210, 14
225, 210, 15
TRUE!
226, 225, 1
227, 225, 2
228, 225, 3
229, 225, 4
230, 225, 5
231, 225, 6
232, 225, 7
233, 225, 8
234, 225, 9
235, 225, 10
236, 225, 11
237, 225, 12
238, 225, 13
239, 225, 14
240, 225, 15
TRUE!
241, 240, 1
242, 240, 2
243, 240, 3
244, 240, 4
245, 240, 5
246, 240, 6
247, 240, 7
248, 240, 8
249, 240, 9
250, 240, 10
251, 240, 11
252, 240, 12
253, 240, 13
254, 240, 14
255, 240, 15
TRUE!
0, 255, -255
1, 255, -254
2, 255, -253
3, 255, -252
4, 255, -251
5, 255, -250
6, 255, -249
7, 255, -248
8, 255, -247
9, 255, -246
10, 255, -245
11, 255, -244
12, 255, -243
13, 255, -242
14, 255, -241
15, 255, -240
16, 255, -239
17, 255, -238
18, 255, -237
19, 255, -236
20, 255, -235
21, 255, -234
22, 255, -233
23, 255, -232
24, 255, -231
25, 255, -230
26, 255, -229
27, 255, -228
28, 255, -227
29, 255, -226
30, 255, -225
31, 255, -224
32, 255, -223
33, 255, -222
34, 255, -221
35, 255, -220
这是上面的 typeNum
函数:
//--------------------------------------------------------------------------------------------
//typeNum (overloaded)
//-see AVRLibC int to string functions: http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html
//--------------------------------------------------------------------------------------------
//UNSIGNED:
void typeNum(uint8_t myNum)
{
char buffer[4]; //3 for the number (up to 2^8 - 1, or 255 max), plus 1 char for the null terminator
utoa(myNum, buffer, 10); //base 10 number system
typeString(buffer);
}
void typeNum(uint16_t myNum)
{
char buffer[6]; //5 for the number (up to 2^16 - 1, or 65535 max), plus 1 char for the null terminator
utoa(myNum, buffer, 10); //base 10 number system
typeString(buffer);
}
void typeNum(uint32_t myNum)
{
char buffer[11]; //10 chars for the number (up to 2^32 - 1, or 4294967295 max), plus 1 char for the null terminator
ultoa(myNum, buffer, 10); //base 10 number system
typeString(buffer);
}
//SIGNED:
void typeNum(int8_t myNum)
{
char buffer[5]; //4 for the number (down to -128), plus 1 char for the null terminator
itoa(myNum, buffer, 10); //base 10 number system
typeString(buffer);
}
void typeNum(int16_t myNum)
{
char buffer[7]; //6 for the number (down to -32768), plus 1 char for the null terminator
itoa(myNum, buffer, 10); //base 10 number system
typeString(buffer);
}
void typeNum(int32_t myNum)
{
char buffer[12]; //11 chars for the number (down to -2147483648), plus 1 char for the null terminator
ltoa(myNum, buffer, 10); //base 10 number system
typeString(buffer);
}
所以我想通了:
答案很简单,但背后的理解却不是。
答案:
(如何修复):
不要使用 (byte)seconds - tStart
,而是使用 (byte)((byte)seconds - tStart)
。而已!问题解决了!您需要做的就是将数学运算(在本例中为减法)的 output 也转换为 byte
,并且它是固定的!否则它 returns 作为 signed int,这会产生错误行为。
那么,为什么会这样呢?
答案:
在 C、C++ 和 C# 中,不存在针对字节的数学运算! 显然,+、- 等运算符所需的汇编级函数不存在对于字节输入。相反,在进行操作之前,首先将所有字节隐式转换(提升)为 int,然后对 ints 进行数学运算,完成后,它 return也是一个int!
因此,此代码 (byte)seconds - tStart
由编译器隐式转换(在本例中提升)如下:(int)(byte)seconds - (int)tStart
...它 return 是一个 int
也。令人困惑,是吗?我当然这么认为!
这里有一些关于这个问题的更多阅读:
(星号、*越多越有用)
- *****byte + byte = int... why? <--特别有用
- *****Implicit type conversion rules in C++ operators <--ESPECIALLY USEFUL. This answer here 显示何时发生隐式强制转换,并指出,“注意。操作的最小大小为
int
。因此short
/char
在操作完成之前被提升为int
。" - *****Google search for "c++ does implicit casting occur with comparisons?"
- https://www.tutorialspoint.com/cprogramming/c_type_casting.htm
- http://www.improgrammer.net/type-casting-c-language/
- Google search for "c++ implicit casting"
- http://www.cplusplus.com/doc/tutorial/typecasting/
- http://en.cppreference.com/w/cpp/language/implicit_conversion
- http://en.cppreference.com/w/cpp/language/operator_comparison
现在让我们看一些真实的 C++ 示例:
这是一个完整的 C++ 程序,您可以编译和 运行 测试表达式以查看 return 类型是什么,以及它是否已被编译器隐式转换为您不知道的类型'我打算:
#include <iostream>
using namespace std;
//----------------------------------------------------------------
//printTypeAndVal (overloaded function)
//----------------------------------------------------------------
//UNSIGNED:
void printTypeAndVal(uint8_t myVal)
{
cout << "uint8_t = " << (int)myVal << endl; //(int) cast is required to prevent myVal from printing as a char
}
void printTypeAndVal(uint16_t myVal)
{
cout << "uint16_t = " << myVal << endl;
}
void printTypeAndVal(uint32_t myVal)
{
cout << "uint32_t = " << myVal << endl;
}
void printTypeAndVal(uint64_t myVal)
{
cout << "uint64_t = " << myVal << endl;
}
//SIGNED:
void printTypeAndVal(int8_t myVal)
{
cout << "int8_t = " << (int)myVal << endl; //(int) cast is required to prevent myVal from printing as a char
}
void printTypeAndVal(int16_t myVal)
{
cout << "int16_t = " << myVal << endl;
}
void printTypeAndVal(int32_t myVal)
{
cout << "int32_t = " << myVal << endl;
}
void printTypeAndVal(int64_t myVal)
{
cout << "int64_t = " << myVal << endl;
}
//FLOATING TYPES:
void printTypeAndVal(float myVal)
{
cout << "float = " << myVal << endl;
}
void printTypeAndVal(double myVal)
{
cout << "double = " << myVal << endl;
}
void printTypeAndVal(long double myVal)
{
cout << "long double = " << myVal << endl;
}
//----------------------------------------------------------------
//main
//----------------------------------------------------------------
int main()
{
cout << "Begin\n\n";
//Test variables
uint8_t u1 = 0;
uint8_t u2 = 1;
//Test cases:
//for a single byte, explicit cast of the OUTPUT from the mathematical operation is required to get desired *unsigned* output
cout << "uint8_t - uint8_t:" << endl;
printTypeAndVal(u1 - u2); //-1 (bad)
printTypeAndVal((uint8_t)u1 - (uint8_t)u2); //-1 (bad)
printTypeAndVal((uint8_t)(u1 - u2)); //255 (fixed!)
printTypeAndVal((uint8_t)((uint8_t)u1 - (uint8_t)u2)); //255 (fixed!)
cout << endl;
//for unsigned 2-byte types, explicit casting of the OUTPUT is required too to get desired *unsigned* output
cout << "uint16_t - uint16_t:" << endl;
uint16_t u3 = 0;
uint16_t u4 = 1;
printTypeAndVal(u3 - u4); //-1 (bad)
printTypeAndVal((uint16_t)(u3 - u4)); //65535 (fixed!)
cout << endl;
//for larger standard unsigned types, explicit casting of the OUTPUT is ***NOT*** required to get desired *unsigned* output! IN THIS CASE, NO IMPLICIT PROMOTION (CAST) TO A LARGER *SIGNED* TYPE OCCURS.
cout << "unsigned int - unsigned int:" << endl;
unsigned int u5 = 0;
unsigned int u6 = 1;
printTypeAndVal(u5 - u6); //4294967295 (good--no fixing is required)
printTypeAndVal((unsigned int)(u5 - u6)); //4294967295 (good--no fixing was required)
cout << endl;
return 0;
}
您也可以在此处在线 运行 此程序:http://cpp.sh/6kjgq
这是输出。请注意,单无符号字节 uint8_t - uint8_t
和双无符号字节 uint16_t - uint16_t
都隐式转换(被 C++ 编译器提升)为 4 字节 signed int32_t
(int
) 变量类型。这是您需要注意的行为。因此,这些减法的结果是 negative,这是最初让我感到困惑的异常行为,因为我曾预料到它会下溢成为无符号变量的最大值(因为我们是做 0 - 1)。 为了实现所需的下溢,我不得不显式将减法的输出结果转换为所需的无符号类型,而不仅仅是 inputs。 但是,对于 unsigned int
的情况,不需要对结果进行显式转换。
Begin
uint8_t - uint8_t:
int32_t = -1
int32_t = -1
uint8_t = 255
uint8_t = 255uint16_t - uint16_t:
int32_t = -1
uint16_t = 65535unsigned int - unsigned int:
uint32_t = 4294967295
uint32_t = 4294967295
这是另一个简短的程序示例,显示单个无符号字节 (unsigned char
) 变量在操作时被提升为有符号整数 (int
)。
#include <stdio.h>
int main(int argc, char **argv)
{
unsigned char x = 130;
unsigned char y = 130;
unsigned char z = x + y;
printf("%u\n", x + y); // Prints 260.
printf("%u\n", z); // Prints 4.
}
输出:
260
4
在这里测试:http://cpp.sh/84eo