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"
}
@wBB, 👍1
3 ответа
Лучший ответ:
В вашем #define B вы пропустили скобку (). Измените определение на:
#define B (A * 62)
Без скобок вы сначала делите 6200000 на 200, а затем умножаете результат на 62, а это не то, что вы хотите.
Макросы с полным заключением в круглые скобки (как указано в ответе 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
Как объяснялось в предыдущих ответах, полное заключение макросов в круглые скобки — это
стандартное решение этой проблемы на 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
- Как читать и записывать EEPROM в ESP8266
- Как сделать выводы Tx и Rx на ESP-8266-01 в выводах GPIO?
- Как навсегда изменить скорость передачи данных ESP8266 (12e)?
- Как заставить 5-вольтовое реле работать с NodeMCU
- Как исправить: Invalid conversion from 'const char*' to 'char*' [-fpermissive]
- ESP8266 не подключается к Wi-Fi
- AT-команда не отвечает на последовательный монитор
- Разница между этими двумя платами NodeMCU?
Чувак, ты прав на 100%. Я потратил несколько часов, пытаясь понять, в чем дело... Огромное спасибо!, @wBB
@wBB помните, что в C макросы заменяются в коде точно так, как вы их написали, на этапе препроцессора *перед* компиляцией кода. Поэтому в таких случаях в качестве проверки работоспособности полезно самостоятельно расширить макросы в своем коде, чтобы увидеть, получаете ли вы то, что намеревались., @brhans
На самом деле к этой проблеме меня привел недостаток опыта работы с C. Спасибо!, @wBB