Непонятное поведение переменной LONG.

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

Друзья, я совсем начинающий, прошу помощи. Пытаюсь написать таймер с обратным отсчетом. Полагаю, что делаю это "в лоб", без изящества, но это мои первые опыты. Столкнулся с непонятным поведением переменной. Данная переменная (в скетче она объявлена как "long sech"). При ее вычислении ("sech=encoder_val*3600;") , если encoder_val меньше 10, то все нормально. Если 10 и больше - значение скидывается на отрицательное число и по мере дальнейшего увеличения encoder_val значение sech растет, но уже начиная с этого отрицательного числа. При этом картина абсолютно одинаковая - что при объявлении sech как long, так и int (в последнем случае - понятно: int больше 32767 "не понимает", но long-то должен это делать!). Где я напортачил? Заранее спасибо!

01//Плата Arduino UNO
02  
03#include "LedControl.h"  //Подключаем библиотеку LedControl.h
04LedControl lc = LedControl(13, 11, 12, 1);  //создаём объект класса LedControl
05 
06//Значения кнопки и данных энкодера
07volatile int button_state = 0;
08volatile int encoder_val = 0;
09 
10//Переменные состояния на выводах энкодера
11unsigned char encoder_A;  
12unsigned char encoder_B;
13unsigned char encoder_A_prev=0;
14 
15//Переменные для индикации
16int dh; //Десятки часов
17int eh; //Единицы часов
18 
19//Переменные для рассчетов
20long sech;   //Количество секунд в часах
21 
22void setup()
23{
24  //Устанавливаем пин для канала В энкодера
25  pinMode(4, INPUT);
26 
27  //Настраиваем пины прерывания
28  attachInterrupt(1, BUTTON, RISING);
29  attachInterrupt(0, ENCODER, RISING);
30   
31  //Инициализация порта вывода
32  Serial.begin (115200);
33   
34  //Инициализация модуля индикаторов
35  lc.shutdown(0, false); //Устройство(7-ми сегментный дисплей) выводим из спящего режима
36  lc.setIntensity(0,3);  //Яркость дисплея на 3. Всего возможных режимов яркости от 0 до 15
37  lc.clearDisplay(0);    //Очистить дисплей
38}
39  
40void loop()
41{
42  if(button_state==0) // Если значение = 0, не делать ничего, просто надпись ("76543210")
43    {
44      for (int dig=7; dig>=0;dig--)
45        {
46          lc.setDigit(0, dig, dig, false);
47          delay (200);    
48        }
49      delay (1000);
50      lc.clearDisplay(0); // Очищаем индикатор
51    }
52     
53  if (button_state==1) // Если значение = 1, установка часов
54    {
55      if (encoder_val>24) {encoder_val=24;}  // Ограничение часов до 24
56      if (encoder_val<0) {encoder_val=0;}    // Ограничение часов до 0
57      dh=encoder_val/10; //Выделяем десятки часов
58      eh=encoder_val%10; //Выделяем единицы часов
59      lc.setDigit(0, 7, dh , false); //Индикация десятков часов  
60      lc.setDigit(0, 6, eh , false); //Индикация единиц часов      
61      sech=encoder_val*3600; // Полученное число секунд в часах (ВОТ ТУТ ЗАГВОЗДКА!)
62      Serial.print(encoder_val);
63      Serial.print(" # ");
64      Serial.println(sech);
65    }
66  
67 }
68 
69void BUTTON() //Перечисление статусов кнопки и работа с каждым из них
70{
71  button_state++;
72  if (button_state>1) {button_state=0;}
73}
74void ENCODER() //Считывание энкодера
75{
76  encoder_B=digitalRead(4);
77  if (encoder_B==HIGH) {encoder_val--;}
78  if (encoder_B==LOW) {encoder_val++;}
79}

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Перед проведением арифметической операции приведите исходную переменную к необходимому типу.

1sech=(long)encoder_val*3600;

Замечу, что для "временнЫх" значений использование знаковых переменных наврядли стоит считать хорошей идеей.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

sadman41 пишет:
Перед проведением арифметической операции приведите исходную переменную к необходимому типу.

1sech=(long)encoder_val*3600;

Замечу, что для "временнЫх" значений использование знаковых переменных наврядли стоит считать хорошей идеей.

Одно из выражений к long.  Или как выше или 

1sech=encoder_val*3600L;
 

 

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

Огромное спасибо! Вечером обязательно применю (работа, чтоб ее...).

А какие переменные (не знаковые) стоит использовать? Я предполагал, что таймер будет просто вычитать из общего исходного (установленного) количества секунд по единичке, вычленять из этого часы, минуты и секнды и индицировать их на семисегментнике. Не больше, не меньше. В этой ситуации (полагаю с высоты своей недообразованности!) нет разницы: все равно работать с положительными числами.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А почему в коде написано "long", а в тексте сообщения - "LONG"?

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

Меня все эти long, int, short итд ужасно раздражают. Каждый раз приходится вспоминать какого же они размера в данной архитектура.

Пользуйтесь лучше, int8_t, uint8_t, int16_t и так далее. Сразу все понятно.

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

Я в них не очень путаюсь: какой-никакой опыт с бэйсиком есть, но за совет спасибо, учту. Просто в данном случае было непонятно, в чем капкан. В VB6 такого не бывает: лонг - это лонг, добл - добл... И никаких дополнительных уточнений в тексте делать не надо. А тут....

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

Для уточнения, в каком месте затыка. Если нарушил правила форума - прошу простить: новичок я...

sadman41
Offline
Зарегистрирован: 19.10.2016

Си дрессирует программиста по полной программе. Приготовьтесь к этому.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

ну дак это С, брат.  У тебя справа от = два выражения, оба типа int, компилятор их и складывает как int, при этом int переполняется и уходит в область отрицательных значений.  Слева от = переменная типа long, поэтому результат расширяется до long с сохранением знака.  Чтоб такого не было, одно из выражений справа надо явно привести к типу long, тогда второе приведется к нему же отоматисски и результат будет правильный. 

sadman41
Offline
Зарегистрирован: 19.10.2016

А вот вопрос, кстати: это правило распространяется на правое выражение любой сложности или же только на два операнда?

Т.е. валидно ли выражение

1sech=((long)encoder_val_01 * 3600) + (encoder_val_02 * 3600);

или encoder_val_02 необходимо тоже к long приводить? Могу ли я на буквочках исходника сыканомить или лучше не дразнить gcc-ей?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Столь далеко мое скудоумие не простираеца, надо спросить ЕвгенийПетровича. 

Но памойму, достаточно чонить одно привести явно, остальные подтянуца

b707
Offline
Зарегистрирован: 26.05.2017

DetSimen пишет:

надо спросить ЕвгенийПетровича. 

 

однозначно спросить надо.

Однако ставлю на то, что приводить надо. Я бы в своем коде приводил оба.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Вы меня троллите?

Понятно, что преобразование делается только по мере необходимости, а значит в выражении (encoder_val_02 * 3600) ничего преобразовываться не будет. Преобразуется только его результат.

Да и проверить это нетрудно:

01#include <Printing.h>
02 
03void setup(void) {
04    const int encoder_val_01 = 30, encoder_val_02 = 30;
05    const long s1 = ((long)encoder_val_01 * 3600) + (encoder_val_02 * 3600);
06    const long s2 = ((long)encoder_val_01 * 3600) + ((long)encoder_val_02 * 3600);
07    Serial.begin(57600);
08    printf("s1=%ld\r\ns2=%ld\r\n", s1, s2);
09}
10 
11void loop(void) {}
12//
13//  Результат
14//
15//  s1=84928
16//  s2=216000
17//
b707
Offline
Зарегистрирован: 26.05.2017

о, я выиграл :)

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

DetSimen пишет:

ну дак это С, брат.  У тебя справа от = два выражения, оба типа int, компилятор их и складывает как int, при этом int переполняется и уходит в область отрицательных значений.  Слева от = переменная типа long, поэтому результат расширяется до long с сохранением знака.  Чтоб такого не было, одно из выражений справа надо явно привести к типу long, тогда второе приведется к нему же отоматисски и результат будет правильный. 

Большое спасибо! Теперь все разжевано, в будущем буду учитывать! Вот, занялся на седьмом десятке, уж вся грудь седая, а все как мальчик :)

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

Дорогие форумчане, всем большое спасибо. Вопрос, как я понимаю, снят. Тему, наверное, можно завершить, чтоб не флудить. Но это на Вашще усмотрение.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

b707 пишет:

о, я выиграл :)

Снимаю шляпу. 

b707
Offline
Зарегистрирован: 26.05.2017

DetSimen пишет:

Снимаю шляпу. 

С кого? :)

просто повезло...

bwn
Offline
Зарегистрирован: 25.08.2014

b707 пишет:

DetSimen пишет:

Снимаю шляпу. 

С кого? :)

А дида то наш, не прост, успел в далекой юности с прохожих, мандатровые, посдергивать. Вот и прет привычка.))))

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

bwn пишет:

b707 пишет:

DetSimen пишет:

Снимаю шляпу. 

С кого? :)

А дида то наш, не прост, успел в далекой юности с прохожих, мандатровые, посдергивать. Вот и прет привычка.))))

тогда б я так и написал. "Сдергиваю"

Но я всего лишь почтительно снимаю свою.  Перед Великими. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

bwn пишет:

А дида то наш, не прост

«П-прекратите д-демагогию! — взорвался наконец и Фёдор Симеонович. — К-как вам не с-совестно нести такую чушь? К-какой я вам п-простой человек? И что это за словечко такое — п-простой? Это д-дубли у нас простые!»

(надеюсь, ичточник не надо указывать? :))

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

[/quote]

(надеюсь, ичточник не надо указывать? :))

[/quote]

Конечно, не надо. Спасибо, повеяло моей юностью.... :)

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

Друзья, всем спасибо! 

"sech=encoder_val*3600L;" мне показалось короче, нежели "sech=(long)encoder_val*3600;", применил первый вариант (сэкономил объем кода). Всё сразу же забегало, заскакало, замигало, заработало :)

Век живи, век учись у умных и образованных людей!

 

 

sadman41
Offline
Зарегистрирован: 19.10.2016

L можно незаметить, а вот (long) уже не пропустишь. Да и не всегда в вычислениях есть константы, к которым можно прилепить L, UL. На объёме результирующего кода это не сказывается. Так что это - дело вкуса.

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

sadman41 пишет:

L можно незаметить, а вот (long) уже не пропустишь. Да и не всегда в вычислениях есть константы, к которым можно прилепить L, UL. На объёме результирующего кода это не сказывается. Так что это - дело вкуса.

Ок, тонкости усёк, буду иметь ввиду.

SLKH
Offline
Зарегистрирован: 17.08.2015

sadman41 пишет:

Си дрессирует программиста по полной программе. Приготовьтесь к этому.

Да. Начинающий программист должен постаянно находиться в состоянии изумления; он никогда не догадается, какие ещё приколы в запасе у дрессировщика и когда он решит применить очередной.