Arduino: неправильный расчет длинного целого числа

Я выполняю простой расчет с целыми числами (на Arduino с ESP8266 12E), но не могу получить ожидаемый результат и не могу найти ошибку. Может ли кто-нибудь помочь мне?

#define A      200
#define B      A * 62
#define C      500

void setup() {
   Serial.begin(9600);
   Serial.println("");

   unsigned long aux = 0;

   aux = (B * 500) / C;    // (12400 * 500) / 500 = 12400
   Serial.printf("aux = %d\n", aux);
   aux = aux * C;          // 12400 * 500 = 6200000
   Serial.printf("aux = %d\n", aux);

   // ОШИБКА: Должно получиться "500", но в результате получается "1922000"
   aux = aux / B;          // 6200000/12400 = 500

   Serial.printf("aux = %d\n", aux); // Печатает "1922000"
}

, 👍1


3 ответа


Лучший ответ:

11

В вашем #define B вы пропустили скобку (). Измените определение на:

#define B      (A * 62)

Без скобок вы сначала делите 6200000 на 200, а затем умножаете результат на 62, а это не то, что вы хотите.

,

Чувак, ты прав на 100%. Я потратил несколько часов, пытаясь понять, в чем дело... Огромное спасибо!, @wBB

@wBB помните, что в C макросы заменяются в коде точно так, как вы их написали, на этапе препроцессора *перед* компиляцией кода. Поэтому в таких случаях в качестве проверки работоспособности полезно самостоятельно расширить макросы в своем коде, чтобы увидеть, получаете ли вы то, что намеревались., @brhans

На самом деле к этой проблеме меня привел недостаток опыта работы с C. Спасибо!, @wBB


3

Макросы с полным заключением в круглые скобки (как указано в ответе dmz) решают один класс проблем.

>

Еще одна вещь, которую вам следует сделать, это в любом арифметическом выражении, которое включает литеральные константы, использовать суффикс L хотя бы для одной из задействованных констант, если есть вероятность, что результат превысит 32767 (максимальное значение). гарантированно представимое значение для int). Тип арифметической операции в C основан только на типах операндов этой операции; тип переменной, которой присваивается результат, не имеет значения.

(править) Например:

long q, r;
unsigned char v = 231;
unsigned char w = 197;

q = v * w * 5;
r = v * w * 5L;

q может содержать 30927, поскольку после обычных арифметических преобразований (6.3.1.8) [в данном случае целочисленных повышений (6.3.1.1-2)] все операнды имеют тип int и INT_MAX может иметь значение 32767, и в этом случае каждая операция будет выполняться по модулю 32768.

r будет содержать 227535, поскольку константа 5L имеет тип long и, следовательно, все операции в этом выражении будут выполняться над значениями введите long.

,

...и спецификатор формата для unsigned long%lu, а не %d - компилятор для esp8266 спрашивающего использует 32-битный int, поэтому им сходят с рук некоторые вещи, которые они не сделали бы на Arduino на базе ATmega, где int — это минимальный размер в 16 бит, разрешенный спецификацией., @Chris Stratton

@mlp обычно я использую приведение типов практически ко всему. Пример: int J = -1, приведение типа (unsigned char) J // печатает 255. Когда вы говорите о суффиксе «Л», что вы имеете в виду? Можете ли вы привести пример?, @wBB


0

Как объяснялось в предыдущих ответах, полное заключение макросов в круглые скобки — это стандартное решение этой проблемы на C. Однако на Arduino вы программирование на C++, а в C++ хорошей практикой считается заменять это использование #define с помощью явных констант:

const int A = 200;
const int B = A * 62;
const int C = 500;

Это не только устраняет первоначальную проблему, но и дает некоторые безопасность типов: вы можете присвоить этим константам другие типы (например, long), если необходимо.

,

Некоторые компиляторы интерпретируют «const» как переменную, попадающую в память программы, что, в свою очередь, приводит к проблемам с пространством памяти. Константы обеспечивают защиту типа, что также может быть (частично) достигнуто путем приведения типов внутри определения, например, «#define A ((int)200)»., @le_top

@le_top: Есть ли у вас конкретный пример «проблем с пространством памяти», которые могут возникнуть? Я сомневаюсь, что вы сможете найти пример, который не вызывает неопределенного поведения., @Edgar Bonet

Что-то вроде этого, например: "const int a=100; int b=200; void setB(const int *c) {b=c;} void ex1() {setB(&a);}" . Но есть и другие случаи. Когда «const» помещает «а» в ПЗУ, некоторые встроенные компиляторы не могут справиться с такого рода присваиванием и не сообщают обо всех случаях нарушения. Поэтому я склонен ставить «CONST», если есть риск в будущем. (если возможно, с «#define CONST const»)., @le_top

@le_top: Я думаю, ты имеешь в виду b=*c. Для меня это звучит как ошибка компилятора. У какого компилятора были проблемы с этим? Я попробовал ваш код на avr-gcc, и проблем не возникло, даже когда я заменил const на __flash const, что привело к помещению a во flash., @Edgar Bonet

Да, б=*с. Это будет зависеть от компилятора и uC. Я сообщаю, когда нахожу ошибки - когда компилятор не предупреждает об этом, это ошибка, но в остальном это документированное ограничение. На Arduino следует использовать PROGMEM, а не __flash. Я не хочу выделять конкретный компилятор, мой комментарий в основном касался предупреждения о том, что существуют компиляторы для встраиваемых систем, которые интерпретируют const таким образом, что нарушает совместимость кода. Именно поэтому вам необходимо добавить «__flash», чтобы получить переменную во FLASH — хранение ее в ОЗУ упрощает генерацию кода (и скорость) для этих процессоров., @le_top