Странная ошибка в моем Arduino ALU
Я обнаружил странную ошибку в своем Arduino. Этого нет в кодексе. Я думаю, что это аппаратная ошибка в ALU микроконтроллера, возможно, только в клоне.
Таким образом, существует переменная feedLimit
, и ее значение равно 30, что на самом деле не имеет значения.
Когда я печатаю ответ feedLimit301000, используя эту команду Serial.println(feedLimit*30*1000)
Ответ, напечатанный на последовательном мониторе, - 30528, что явно неверно. Но когда я печатаю feedLimit*30000, ответ правильный (300000). Ответ должен быть одинаковым в обоих случаях, но это не так. Я приложил код, а также скриншот вывода.
Может кто-нибудь, пожалуйста, дайте мне знать, если это ошибка в Arduino или что-то еще? Я никогда не сталкивался с такой ошибкой на аппаратном уровне.
Спасибо.
int interval, interval2;
int feedingTime = 3; //Должен кормить один раз каждые 3 минуты
int feedLimit = 30; //Если кормить 10 раз или более в течение промежутка времени кормления, фиш умирает
int dieaffter = 30; //Умрет, если его не кормить в течение 10 минут
int feedCounter = 0; //Подсчитывает, сколько раз кормят в промежутке времени кормления
void setup() {
Serial.begin(9600);
pinMode(btnPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
Serial.println(feedLimit);
Serial.println(feedLimit*60*1000);
Serial.println(feedLimit*60000);
}
@Z Dhillon, 👍0
Обсуждение1 ответ
Лучший ответ:
Это не ошибка. Просто вы работаете со знаковыми 16-битными целыми числами на 8-битном микроконтроллере. Максимум, что может хранить int, - 32767, а литералы по умолчанию имеют 16-битный знак.
Ваши три отпечатка:
Serial.println(feedLimit);
Печать 30.
Serial.println(feedLimit*60*1000);
Умножьте feedLimit
на 60 = 1800. Кратно 1800 на 1000 = 1800000. Поместите 1800000 в 16-битный знак = 30538. Также работает по-другому: препроцессор умножает два 16-битных int, чтобы создать новую 16-битную константу int: 60 * 1000 = 60000, которая слишком велика, чтобы поместиться в int, и усекается до 27232. Умножьте на 30 = 30538 при усечении до 16 бит.1
Serial.println(feedLimit*60000);
Умножьте 30 на 60000. 60000 слишком велик для хранения в int, поэтому он неявно повышается до long. 30 * 60000 = 1800000. В результате получается long, потому что в вашем расчете есть неявное long.
Вы должны быть осторожны с большими числами на небольших микроконтроллерах и иметь в виду пределы каждого типа переменных, а также тип данных по умолчанию для литералов.
- char: от -128 до +127
- unsigned char / byte: от 0 до 255
- int: от -32,768 до +32,767
- unsigned int: от 0 до 65 535
- длинные позиции: от -2,147,483,648 до 2,147,483,647
- unsigned long: от 0 до 4,294,967,295
Вы можете заставить литералы быть определенным типом данных, добавив к ним суффикс:
- L: Длинный
- UL: unsigned long
(Есть и другие, но они используются реже).
Таким образом, ваше первое (неудачное) вычисление можно исправить, заставив одно из значений быть длинным:
Serial.println(feedLimit*60*1000L);
1: Кажется, avr-gcc недостаточно умен, чтобы сделать это, вместо этого он всегда обрабатывает каждую операцию отдельно, и каждый раз он работает от int-to-int, используя два 8-битных регистра для представления каждого значения или результата.
Это было очень полезно. Спасибо :), @Z Dhillon
В качестве альтернативы можно использовать типы с явными размерами: int8_t
, int16_t
, int32_t
и т. Д. Они будут иметь ожидаемый размер независимо от того, какой процессор используется., @PMF
Я не думаю, что свернутая константа 60000 усекается до 27232. Вы проверили это в скомпилированном коде?, @the busybee
@thebusybee Кажется, avr-gcc недостаточно умен, чтобы сложить 60 * 1000 как константы в одну константу 60000. Вместо этого он выполняет отдельные вызовы " mul "во время выполнения - "<variable>* 60", за которым следует "<result of previous>* 1000". И на каждом этапе он работает всего с 2 8-битными регистрами на значение (или просто 1 в случае 60, конечно, это не так глупо), и все результаты каждый раз попадают в два регистра. Все усекается и снова усекается. Но он не думает объединять литералы таким образом..., @Majenko
Re “Кажется, avr-gcc недостаточно умен, чтобы сложить 60 * 1000 как константы в одну константу 60000”: Он не может этого сделать: это нарушило бы правила языка о неявной типизации. Но это умно: он замечает, что "feedLimit" не меняется, и складывает " feedLimit*60*1000` в 30528. Никаких вызовов " mul " во время выполнения., @Edgar Bonet
@EdgarBonet Не в соответствии с выводом "avr-gcc-S" в моих тестах. Там был длинный список инструкций "мул", чтобы получить результат., @Majenko
Ммм... странно. Avr-g++ 7.3.0, поставляемый с Arduino 1.8.15, выполняет оптимизацию. Вы компилировали с помощью -Os -flto
?, @Edgar Bonet
@EdgarBonet Нет, я этого не делал. Я бы не ожидал, что-flto будет иметь какой-либо эффект, так как нет никакой связи. -Ос *может быть* но кто знает? В любом случае это не имеет значения, поскольку точное "почему" не имеет значения. Что бы компилятор ни решил сделать или не решил сделать, это все равно 16-битные целые числа, если только нет веской причины для его продвижения в long., @Majenko
@Majenko вчера вечером у меня тоже были проблемы с попыткой воспроизвести то, что вы видели в folding. Но мои мысли по этому поводу были больше о том, что независимо от того, что он делает, вы просите его сделать что-то неопределенное, и в таких случаях трудно ожидать оптимизатора. Я бы проверил -fwrapv
, чтобы проверить наличие изменений. Но ни первоначальной проблемы, ни теста. Как вы сказали, в конечном счете это не имеет значения. **Единственное, что я хотел добавить, это то, что указанная база литерала влияет на то, считаются ли кандидатами типы unsigned
**. Например, 32768
-это долго, но 0x8000
- это unsigned int
с 16-битными системами., @timemage
- Как погасить светодиод за определенное время с помощью FastLED
- Чтение и запись в EEPROM
- Соединение ATTiny85 I2C с Arduino Nano
- Функция random() вообще не случайна
- Возможно ли передавать данные с Arduino Nano на сервер Websocket через USB?
- Кто-нибудь может привести пример извлечения данных из массива char?
- Arduino работает правильно, только когда я прикасаюсь к нему
- Проблема с чтением данных из ELM327
Это не жук. Вы работаете с целыми числами на 8 - битном микроконтроллере. Максимум, что они могут вместить, - это 32767., @Majenko