Embedded C Cortex-M4: правильно ли обрабатывать деление на большие числа? - PullRequest
2 голосов
/ 27 апреля 2019

Я пишу встроенный код для спектроскопии.Чтобы построить мой спектр, мне нужно отобразить линейные выборки из интервала (динамический диапазон задается физикой / спецификациями задачи) в другой.В основном после обработки данных у меня есть серия выборок (пиков), и каждый из них будет вносить свой вклад в спектр (т.е. будет увеличивать счетчик определенного бина в гистограмме).Вот эскиз: Peaks to Hist mapping Так что в C мне нужно отобразить каждое пиковое значение в [0: 4095], и я делаю это в режиме реального времени на MCU (LPC4370), поэтому мне нужноидти быстро.Проблема в том, что моя тупая реализация сжала все до 0. Вот что я сделал:

 #define MCA_SIZE     4096
 #define PEAK_MAX     1244672762
 #define PEAK_MIN     6000000

 int32_t mca[MCA_SIZE];
 int32_t peak_val;
 int32_t bin_val;

[...]

 if(peak_val > PEAK_MIN)
      {
       bin_val = (int)(MCA_SIZE*(peak_val-PEAK_MIN)/(PEAK_MAX-PEAK_MIN));

       /*Increment corrispondent multi channel bin*/
       mca[bin_val]+=1;
      };

Где каждое значение равно int32, если нижнее значение cas, #define это верхний регистр.Проблема в том, что, поверьте, этот

(peak_val-PEAK_MIN)/(PEAK_MAX-PEAK_MIN)

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

Вот скриншот первых значений mca после нескольких тысяч итераций: enter image description here

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

Disassembly view

Каков наилучший / самый быстрый способ решения этой проблемы

Ответы [ 2 ]

3 голосов
/ 28 апреля 2019

Промежуточный результат (MCA_SIZE*(peak_val-PEAKMIN)) слишком велик для 32-разрядного целочисленного типа данных. Я бы использовал uint64_t для этих вычислений и определил бы все ваши константы как const uint64_t вместо использования #define, добавив суффикс ULL к их буквальным значениям.

1 голос
/ 27 апреля 2019

Обратите внимание, что ваш код может привести к переполнению целых чисел со знаком, которое не определено в стандарте.

Из стандарта C99 (§3.4.3 / 1)

Примером неопределенного поведения является поведение на целое число в течение потока

Так что я бы начал там, либо перешел бы к беззнаковому, использовал бы более широкий тип или изменил границы.

Также, как user6556709 упомянуто в комментарии, выражение:

(MCA_SIZE*(peak_val-PEAK_MIN)/(PEAK_MAX-PEAK_MIN))

Гарантируется, что будет выполнено, как если бы оно было написано следующим образом, из-за ассоциативности слева направо для этой группы операторов (обратите внимание на круглые скобки):

((MCA_SIZE*(peak_val-PEAK_MIN))/(PEAK_MAX-PEAK_MIN))

Таким образом, выражение (peak_val-PEAK_MIN)/(PEAK_MAX-PEAK_MIN), вычисляемое всегда с нуля, не выполняется, выражение (MCA_SIZE*(peak_val-PEAK_MIN)) выполняется ранее, так что это не главная проблема.

Я бы рекомендовал привести несколько примеров для peak_val, в которых корзины не заполнены.

...