Проблема в определении переменной как умножения

У меня есть простейший код для включения и выключения светодиода. Определение времени удержания переменной — onTime с использованием Arduino Nano.

Если написано так: onTime=1000*30, все в порядке.

Когда onTime больше этого значения, цикл if не возвращает значение true, когда это необходимо (см. код ниже).

НО при написании неявно как onTime=60000, а не onTime=1000*60 код работает нормально.

Я предполагаю, что значение 30*1000 должно быть чем-то со значением int.

КОД:

int enbPin = 10;
bool onState = true;
unsigned long onPeriod = 60*1000; // <---- 
unsigned long startTime = millis();
void setup() {
  // put your setup code hee, to run once:
  Serial.begin(9600);
  pinMode(enbPin, OUTPUT);
  digitalWrite(enbPin, onState);
  Serial.println("START");
}

void loop() {
  // put your main code here, to run repeatedly:
  if (millis() - startTime >= onPeriod ) {
    onState = !onState;
    startTime = millis();
    digitalWrite(enbPin, onState);
    Serial.println(onState);
  }
}

, 👍0

Обсуждение

в вашем коде нет переменной с именем onTime, @jsotola


2 ответа


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

4

unsigned long onPeriod = 60ul * 1000;

ul превращает 60 в unsigned long, после чего вычисление имеет место для результата.

без ul константы и результат равны int и он переполняется

,

Спасибо за Ваш быстрый ответ. 1) при использовании NodeMCU без ul результат был в порядке, почему так? 2) чтобы определить 1 час, я должен написать «60ul*1000*60»?, @Guy . D

1) NodeMCU — это 32-битный чип. int имеет длину 32 бита и может содержать гораздо больше (так же, как и long). 2) Конечно. Любое (или все) число может иметь ul, если хотя бы одно из них., @Majenko

Большое спасибо за исчерпывающий ответ, @Guy . D


1

Юрай уже рассказал вам, как это исправить.

Подробнее о том, почему:

(отредактировано)

По умолчанию компилятор C/C++ использует наименьший размер, который может содержать указанное значение для целочисленных констант, начиная с "native" целочисленный размер на платформе, для которой он компилируется. Для 16-битного устройства, такого как Arduino на основе AVR, int составляет 16 бит. 16-битное целое число со знаком может содержать значение от -32768 до 32767.

(Как указал AnT в своем комментарии, если вы объявляете целочисленную константу 60 000, компилятор автоматически повышает ее до более крупного типа long int.)

Он оценивает выражения, предполагая, что константы типа int имеют тип int, если только их значение не помещается в тип int. Если вы попытаетесь выполнить математические операции с int и long int или сохранить результаты математических вычислений int в long, то int будет "повышен". сначала к большему типу данных.

В вашем выражении:

unsigned long onPeriod = 60 * 1000;

Компилятор сначала оценивает правую часть выражения, 60 * 1000, как (int)60 * (int)1000). Поскольку он обрабатывает обе части этого выражения как 16-битные целые числа со знаком, он выполняет умножение, используя математику 16-битных целых чисел. Результат умножения равен 60 000, что слишком велико для хранения в целочисленном значении со знаком, поэтому промежуточные вычисления переполняются.

Затем он пытается сохранить результат в unsigned long. Он преобразует переполненное значение в unsigned long и сохраняет результат (искаженный и, следовательно, неверный) в unsigned long.

Выражая одно или оба значения как константу unsigned long или приводя одно или оба из них к типу unsigned long, компилятор преобразует оба значения в unsigned long, прежде чем выполнять над ними математические операции, поэтому результат не t переполняется перед назначением переменной назначения.

,

«По умолчанию компилятор C/C++ использует «собственный» размер для целочисленных констант». Это немного вводит в заблуждение. Для десятичного литерала без суффикса компилятор автоматически выберет наименьший тип из последовательности int -> long -> long long, который может вместить константу. В данном случае int выбран только потому, что в него подходят константы. А int на этой платформе является 16-битным подписанным типом. Более уместно сказать, что компилятор использует «родной» размер для типа int, а не для целочисленных констант. Например, «2 * 40000» не будет переполняться, даже если «80 * 1000» переполнится., @AnT

@Муравей. Хорошая точка зрения. Я немного боролся с тем, как это сказать. Ваша формулировка точнее., @Duncan C