[英]32b multiplication in 8b uC with avr-g++ vs 32b multiplication on X86 with gcc
我正在做一個定點 C++ 類來在 8b 微控制器上執行一些閉環控制系統。
我編寫了一個 C++ 類來封裝 PID,並使用現代 gcc 編譯器在 X86 桌面上測試了該算法。 都好。
當我使用現代 avr-g++ 編譯器在 8b 微控制器上編譯相同的代碼時,我得到了奇怪的人工制品。 經過一番調試,問題是16b*16b的乘法被截斷為16b。 下面是一些最小的代碼來顯示我正在嘗試做的事情。
我在桌面系統上使用了 -O2 優化,在嵌入式系統上使用了 -OS 優化,沒有其他編譯器標志。
#include <cstdio>
#include <stdint.h>
#define TEST_16B true
#define TEST_32B true
int main( void )
{
if (TEST_16B)
{
int16_t op1 = 9000;
int16_t op2 = 9;
int32_t res;
//This operation gives the correct result on X86 gcc (81000)
//This operation gives the wrong result on AVR avr-g++ (15464)
res = (int32_t)0 +op1 *op2;
printf("op1: %d | op2: %d | res: %d\n", op1, op2, res );
}
if (TEST_32B)
{
int16_t op1 = 9000;
int16_t op2 = 9;
int32_t res;
//Promote first operand
int32_t promoted_op1 = op1;
//This operation gives the correct result on X86 gcc (81000)
//This operation gives the correct result on AVR avr-g++ (81000)
res = promoted_op1 *op2;
printf("op1: %d | op2: %d | res: %d\n", promoted_op1, op2, res );
}
return 0;
}
只需使用局部變量將一個操作數提升為 32b 就足以解決問題。
我的期望是 C++ 會保證數學運算將以與第一個操作數相同的寬度執行,所以在我看來res = (int32_t)0 +...
應該告訴編譯器后面的任何內容都應該在int32_t 分辨率。
這不是發生的事情。 (int16_t)*(int16_t) 操作被截斷為 (int16_t)。
gcc 在 X86 機器中的內部字寬至少為 32b,所以這可能是我在桌面上沒有看到人工制品的原因。
E:\\Programs\\AVR\\7.0\\toolchain\\avr8\\avr8-gnu-toolchain\\bin\\avr-g++.exe$(QUOTE) -funsigned-char -funsigned-bitfields -DNDEBUG -I"E:\\Programs\\AVR\\7.0\\Packs\\atmel\\ATmega_DFP\\1.3.300\\include" -Os -ffunction-sections -fdata-sections -fpack-struct -fshort-enums -Wall -pedantic -mmcu=atmega4809 -B "E:\\Programs\\AVR\\7.0\\Packs\\atmel\\ATmega_DFP\\1.3.300\\gcc\\dev\\atmega4809" -c -std=c++11 -fno-threadsafe-statics -fkeep-inline-functions -v -MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)" -o "$@" "$<"
這是兼容 C++ 編譯器的實際預期行為,意味着我做錯了,還是 avr-g++ 編譯器的一個怪癖?
各種解決方案的調試器輸出
這是編譯器的預期行為。
當您編寫A + B * C
,由於運算符優先級的原因,它等效於A + (B * C)
。 B * C
項是單獨計算的,不考慮以后將如何使用。 (否則,很難查看 C/C++ 代碼並了解實際會發生什么。)
C/C++ 標准中有整數提升規則,有時可以通過在執行乘法之前將 B 和 C 提升為int
類型或unsigned int
類型來幫助您。 這就是為什么您在 x86 gcc 上獲得預期結果的原因,其中int
有 32 位。 但是,由於 avr-gcc 中的int
只有 16 位,因此整數提升對您來說還不夠好。 因此,您需要將B
或C
轉換為int32_t
以確保乘法的結果也是int32_t
。 例如,您可以執行以下操作:
A + (int32_t)B * C
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.