предотвратить переполнение арифметики c - PullRequest
0 голосов
/ 19 июня 2020

Я хочу рассчитать напряжение, используя AD C периферийное устройство PIC18F14K50. Результат находится в диапазоне от 0 до 1023 (10 бит). Поэтому я использовал этот простой расчет:

uint16_t voltage = ADC_Result * 5000 / 1023;

Однако результаты неверны. Я предполагаю, что произошло переполнение арифметики c. Я пробовал множество комбинаций скобок, меняя порядок элементов и т. Д. c.
Лучший результат был 4088, когда ADC_Result было 1023, используя приведенный ниже код; что действительно далеко от 5000.

uint16_t voltage = ADC_Result * (5000 / 1023);

Что мне делать, чтобы получить лучшие результаты в приведенном выше расчете? Пожалуйста, не предлагайте числа с плавающей запятой, так как они вызывают сбой в MCU! Они используют много ресурсов без какой-либо реальной выгоды.

Ответы [ 3 ]

2 голосов
/ 19 июня 2020

Вы можете использовать более широкий тип для промежуточного умножения, например:

uint16_t voltage = (uint32_t)ADC_Result * 5000 / 1023;

РЕДАКТИРОВАТЬ

Если деление на 1023 слишком медленное, вы можете получить примерно равное преобразование, изменив 5000/1023 на 5005/1024, что может использовать быстрый битовый сдвиг для деления :

uint16_t voltage = (uint32_t)ADC_Result * 5005 >> 10;

NB 1023 * 5005/1024 ≃ 5000,1123

0 голосов
/ 20 июня 2020

Что мне делать, чтобы получить лучшие результаты в приведенном выше расчете?

Переполнение кода OP 1`6-битная математика.


Чтобы получить правильный и округленный результат использует более широкую математику и смещение.

// uint16_t voltage = ADC_Result * 5000 / 1023;
uint16_t voltage = (ADC_Result * 5000LU + 1024u/2) / 1024u;
// or
#include <stdint.h>
...
uint16_t voltage = (ADC_Result * UINT32_C(5000) + 1024u/2) / 1024u;

L в 5000LU обеспечивает как минимум 32-битное математическое значение.

Используйте U для потенциально более простых / быстрых вычислений и более простого округления задано ADC_Result не является отрицательным.

+ 1024/2 производит округление до ближайшего, а не усечение.

Используйте 1024 вместо 1023 для правильного масштабирование с учетом обычных характеристик аналого-цифровых преобразователей. Дополнительное преимущество: более быстрое деление, поскольку 1024 - это степень двойки.

0 голосов
/ 19 июня 2020

Вы должны использовать более широкий целочисленный тип для этого вычисления, например uint32_t.

В вашем случае 1023 * 5000 == 3192 (потому что реальный результат 5115000 не подходит), так что это неверно. 5000 / 1023 == 4, что является ожидаемым результатом для целочисленного деления. Разделение ADC_Result на 1023 приведет к тому же поведению.

Вы можете вычислить это в uint32_t, а затем проверить, подходит ли оно к uint16_t:

uint32_t result_tmp = ADC_Result * (5000 / 1023);
uint16_t result;

if (result > 0xffff) {
    // this won't fit
} else {
    result = (uint16_t) result_tmp;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...