Return 值不符合预期。 AVR C计算
Return value not as expected. AVR C calculation
我正在尝试使用 ATmega328P MCU 执行以下计算。
= 1000 · 0 + 2000 · 1 +⋯+ 8000 · 7 / 0+1+⋯+7
在主例程中(如此处所示):
int main(void)
{
//variables
uint16_t raw_values[8];
uint16_t position = 0;
uint16_t positions[8];
char raw[] = " raw";
char space[] = ", ";
char channelString[] = "Channel#: ";
char positionString[] = "Position: ";
//initialize ADC (Analog)
initADC();
//initialize UART
initUART(BAUD, DOUBLE_SPEED);
//give time for ADC to perform & finish 1st conversion
//8us x 25 = 200us
delay_us(200);
while(1)
{
//get the raw values from the ADC for each channel
for(uint8_t channel = 0; channel < 8; channel++)
{
raw_values[channel] = analog(channel);
//invert the raw value
raw_values[channel] = DIVISOR - raw_values[channel];
}
for(uint8_t channel = 0; channel < 8; channel++)
{
//print the channel#
transmitString(channelString);
printDec16bit(channel);
transmitString(space);
//print the raw value from the ADC conversion
printDec16bit(raw_values[channel]);
transmitString(raw);
transmitString(space);
//calculate the position value at each sensor
transmitString(positionString);
positions[channel] = (uint16_t)((POSITION_REF/DIVISOR) * raw_values[channel]);
printDec16bit(positions[channel]);
printCR();
}
printCR();
//calculate and display 'position'
position = calculatePosition(positions);
printDec16bit(position);
printCR();
printCR();
//add a delay
delay_ms(2000);
}
}
我正在调用以下函数,但我得到的 return 值有很大偏差。
uint16_t calculatePosition(uint16_t* channel_positions)
{
uint32_t intermediates[8];
uint32_t temp_sum = 0;
uint16_t divisor = 0;
uint16_t value = 0;
for(uint8_t i = 0; i < 8; i++)
{
intermediates[i] = channel_positions[i] * ((i + 1) * 1000);
}
for(uint8_t j = 0; j < 8; j++)
{
temp_sum = temp_sum + intermediates[j];
}
for(uint8_t k = 0; k < 8; k++)
{
divisor = divisor + channel_positions[k];
}
value = temp_sum/divisor;
return value;
}
或者,我什至尝试过这段代码,得到的结果不是我所期望的。
uint16_t calculatePosition(uint16_t* channel_positions)
{
uint16_t position;
position = ((1000 * channel_positions[0]) +
(2000 * channel_positions[1]) +
(3000 * channel_positions[2]) +
(4000 * channel_positions[3]) +
(5000 * channel_positions[4]) +
(6000 * channel_positions[5]) +
(7000 * channel_positions[6]) +
(8000 * channel_positions[7])) /
(channel_positions[0] +
channel_positions[1] +
channel_positions[2] +
channel_positions[3] +
channel_positions[4] +
channel_positions[5] +
channel_positions[6] +
channel_positions[7]);
return position;
}
我做错了什么?对于诸如 {15, 12, 5, 16, 11, 35, 964, 76} 之类的值数组,我期望得到 6504 的结果,但我得到的是 200 的值(或其他一些奇怪的值)。
查看您的输入数组:{15, 12, 5, 16, 11, 35, 964, 76}
具体来说,查看 964 的元素。该元素乘以 7000 是 6748000,这大于 uint16_t
可以处理。
有多种解决方案。其中之一正在更改为 uint32_t
。如果这不是一个选项,您可以提取因子 1000,如下所示:
position = 1000 *(
((1 * channel_positions[0]) +
(2 * channel_positions[1]) +
(3 * channel_positions[2]) +
(4 * channel_positions[3]) +
(5 * channel_positions[4]) +
(6 * channel_positions[5]) +
(7 * channel_positions[6]) +
(8 * channel_positions[7])) /
(channel_positions[0] +
channel_positions[1] +
channel_positions[2] +
channel_positions[3] +
channel_positions[4] +
channel_positions[5] +
channel_positions[6] +
channel_positions[7]));
请注意,这不会消除问题,但可能会减少问题,以便在合理的输入下不会出现问题。
对循环版本采用相同的思路,我们得到:
uint16_t calculatePosition(uint16_t* channel_positions)
{
uint16_t temp_sum = 0;
uint16_t divisor = 0;
for(uint8_t i = 0; i < 8; i++) {
temp_sum += (channel_positions[i] * (i+1));
divisor += channel_positions[i];
}
return 1000*(temp_sum/divisor);
}
请注意,由于使用整数除法进行舍入,您在此过程中会损失一些准确性。由于您在指定宽度时非常小心,我假设您不愿意更改输入数组的类型。此代码应该以最少的额外内存使用量为您提供最大的准确性。但是,如果您 运行 此函数经常在 16 位计算机上运行,它会对性能产生相当大的影响。
uint16_t calculatePosition(uint16_t* channel_positions)
{
// Use 32 bit for these
uint32_t temp_sum = 0;
uint32_t divisor = 0;
for(uint8_t i = 0; i < 8; i++) {
// Copy the value to a 32 bit number
uint32_t temp_pos = channel_positions[i];
temp_sum += temp_pos * (i+1);
divisor += temp_pos;
}
// Moved parenthesis for better accuracy
return (1000*temp_sum) / divisor;
}
如果结果可以放在 uint16_t
中,这个版本失败的可能性绝对为零,因为 1000*temp_sum
的最大可能值是 2,359,260,000
并且最大值它可以容纳 4,294,967,295
.
关于 MRE 的旁注(最小的、可重现的示例)
MRE:s 描述如下:https://whosebug.com/help/minimal-reproducible-example
在这个例子中,问题中 post 的主要函数是:
#include <stdio.h>
int main()
{
uint16_t positions[] = {15, 12, 5, 16, 11, 35, 964, 76};
uint16_t pos = calculatePosition(positions);
printf("%d\n", pos);
}
足以证明您遇到的问题,仅此而已。
正如所说,问题出在整数溢出上。
在使用整数数学时,将乘数移到外面时要小心! (A * 1000) / B
不等于 (A / B) * 1000
.
最简单的解决方案,将每个操作中的第一个操作数转换为更宽的类型。其他人将被隐式转换。 E.q.
...
position = ((1000UL * channel_positions[0]) +
(2000UL * channel_positions[1]) +
(3000UL * channel_positions[2]) +
(4000UL * channel_positions[3]) +
(5000UL * channel_positions[4]) +
(6000UL * channel_positions[5]) +
(7000UL * channel_positions[6]) +
(8000UL * channel_positions[7])) /
((uint32_t)channel_positions[0] +
channel_positions[1] + // no need to convert, it will be converted implicitly
channel_positions[2] + // since previous operand is wider
channel_positions[3] +
channel_positions[4] +
channel_positions[5] +
channel_positions[6] +
channel_positions[7]);
我正在尝试使用 ATmega328P MCU 执行以下计算。
= 1000 · 0 + 2000 · 1 +⋯+ 8000 · 7 / 0+1+⋯+7
在主例程中(如此处所示):
int main(void)
{
//variables
uint16_t raw_values[8];
uint16_t position = 0;
uint16_t positions[8];
char raw[] = " raw";
char space[] = ", ";
char channelString[] = "Channel#: ";
char positionString[] = "Position: ";
//initialize ADC (Analog)
initADC();
//initialize UART
initUART(BAUD, DOUBLE_SPEED);
//give time for ADC to perform & finish 1st conversion
//8us x 25 = 200us
delay_us(200);
while(1)
{
//get the raw values from the ADC for each channel
for(uint8_t channel = 0; channel < 8; channel++)
{
raw_values[channel] = analog(channel);
//invert the raw value
raw_values[channel] = DIVISOR - raw_values[channel];
}
for(uint8_t channel = 0; channel < 8; channel++)
{
//print the channel#
transmitString(channelString);
printDec16bit(channel);
transmitString(space);
//print the raw value from the ADC conversion
printDec16bit(raw_values[channel]);
transmitString(raw);
transmitString(space);
//calculate the position value at each sensor
transmitString(positionString);
positions[channel] = (uint16_t)((POSITION_REF/DIVISOR) * raw_values[channel]);
printDec16bit(positions[channel]);
printCR();
}
printCR();
//calculate and display 'position'
position = calculatePosition(positions);
printDec16bit(position);
printCR();
printCR();
//add a delay
delay_ms(2000);
}
}
我正在调用以下函数,但我得到的 return 值有很大偏差。
uint16_t calculatePosition(uint16_t* channel_positions)
{
uint32_t intermediates[8];
uint32_t temp_sum = 0;
uint16_t divisor = 0;
uint16_t value = 0;
for(uint8_t i = 0; i < 8; i++)
{
intermediates[i] = channel_positions[i] * ((i + 1) * 1000);
}
for(uint8_t j = 0; j < 8; j++)
{
temp_sum = temp_sum + intermediates[j];
}
for(uint8_t k = 0; k < 8; k++)
{
divisor = divisor + channel_positions[k];
}
value = temp_sum/divisor;
return value;
}
或者,我什至尝试过这段代码,得到的结果不是我所期望的。
uint16_t calculatePosition(uint16_t* channel_positions)
{
uint16_t position;
position = ((1000 * channel_positions[0]) +
(2000 * channel_positions[1]) +
(3000 * channel_positions[2]) +
(4000 * channel_positions[3]) +
(5000 * channel_positions[4]) +
(6000 * channel_positions[5]) +
(7000 * channel_positions[6]) +
(8000 * channel_positions[7])) /
(channel_positions[0] +
channel_positions[1] +
channel_positions[2] +
channel_positions[3] +
channel_positions[4] +
channel_positions[5] +
channel_positions[6] +
channel_positions[7]);
return position;
}
我做错了什么?对于诸如 {15, 12, 5, 16, 11, 35, 964, 76} 之类的值数组,我期望得到 6504 的结果,但我得到的是 200 的值(或其他一些奇怪的值)。
查看您的输入数组:{15, 12, 5, 16, 11, 35, 964, 76}
具体来说,查看 964 的元素。该元素乘以 7000 是 6748000,这大于 uint16_t
可以处理。
有多种解决方案。其中之一正在更改为 uint32_t
。如果这不是一个选项,您可以提取因子 1000,如下所示:
position = 1000 *(
((1 * channel_positions[0]) +
(2 * channel_positions[1]) +
(3 * channel_positions[2]) +
(4 * channel_positions[3]) +
(5 * channel_positions[4]) +
(6 * channel_positions[5]) +
(7 * channel_positions[6]) +
(8 * channel_positions[7])) /
(channel_positions[0] +
channel_positions[1] +
channel_positions[2] +
channel_positions[3] +
channel_positions[4] +
channel_positions[5] +
channel_positions[6] +
channel_positions[7]));
请注意,这不会消除问题,但可能会减少问题,以便在合理的输入下不会出现问题。
对循环版本采用相同的思路,我们得到:
uint16_t calculatePosition(uint16_t* channel_positions)
{
uint16_t temp_sum = 0;
uint16_t divisor = 0;
for(uint8_t i = 0; i < 8; i++) {
temp_sum += (channel_positions[i] * (i+1));
divisor += channel_positions[i];
}
return 1000*(temp_sum/divisor);
}
请注意,由于使用整数除法进行舍入,您在此过程中会损失一些准确性。由于您在指定宽度时非常小心,我假设您不愿意更改输入数组的类型。此代码应该以最少的额外内存使用量为您提供最大的准确性。但是,如果您 运行 此函数经常在 16 位计算机上运行,它会对性能产生相当大的影响。
uint16_t calculatePosition(uint16_t* channel_positions)
{
// Use 32 bit for these
uint32_t temp_sum = 0;
uint32_t divisor = 0;
for(uint8_t i = 0; i < 8; i++) {
// Copy the value to a 32 bit number
uint32_t temp_pos = channel_positions[i];
temp_sum += temp_pos * (i+1);
divisor += temp_pos;
}
// Moved parenthesis for better accuracy
return (1000*temp_sum) / divisor;
}
如果结果可以放在 uint16_t
中,这个版本失败的可能性绝对为零,因为 1000*temp_sum
的最大可能值是 2,359,260,000
并且最大值它可以容纳 4,294,967,295
.
关于 MRE 的旁注(最小的、可重现的示例)
MRE:s 描述如下:https://whosebug.com/help/minimal-reproducible-example
在这个例子中,问题中 post 的主要函数是:
#include <stdio.h>
int main()
{
uint16_t positions[] = {15, 12, 5, 16, 11, 35, 964, 76};
uint16_t pos = calculatePosition(positions);
printf("%d\n", pos);
}
足以证明您遇到的问题,仅此而已。
正如所说,问题出在整数溢出上。
在使用整数数学时,将乘数移到外面时要小心! (A * 1000) / B
不等于 (A / B) * 1000
.
最简单的解决方案,将每个操作中的第一个操作数转换为更宽的类型。其他人将被隐式转换。 E.q.
...
position = ((1000UL * channel_positions[0]) +
(2000UL * channel_positions[1]) +
(3000UL * channel_positions[2]) +
(4000UL * channel_positions[3]) +
(5000UL * channel_positions[4]) +
(6000UL * channel_positions[5]) +
(7000UL * channel_positions[6]) +
(8000UL * channel_positions[7])) /
((uint32_t)channel_positions[0] +
channel_positions[1] + // no need to convert, it will be converted implicitly
channel_positions[2] + // since previous operand is wider
channel_positions[3] +
channel_positions[4] +
channel_positions[5] +
channel_positions[6] +
channel_positions[7]);