Непонятное поведение переменной LONG.
- Войдите на сайт для отправки комментариев
Друзья, я совсем начинающий, прошу помощи. Пытаюсь написать таймер с обратным отсчетом. Полагаю, что делаю это "в лоб", без изящества, но это мои первые опыты. Столкнулся с непонятным поведением переменной. Данная переменная (в скетче она объявлена как "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 |
04 | LedControl lc = LedControl(13, 11, 12, 1); //создаём объект класса LedControl |
05 |
06 | //Значения кнопки и данных энкодера |
07 | volatile int button_state = 0; |
08 | volatile int encoder_val = 0; |
09 |
10 | //Переменные состояния на выводах энкодера |
11 | unsigned char encoder_A; |
12 | unsigned char encoder_B; |
13 | unsigned char encoder_A_prev=0; |
14 |
15 | //Переменные для индикации |
16 | int dh; //Десятки часов |
17 | int eh; //Единицы часов |
18 |
19 | //Переменные для рассчетов |
20 | long sech; //Количество секунд в часах |
21 |
22 | void 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 | |
40 | void 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 |
69 | void BUTTON() //Перечисление статусов кнопки и работа с каждым из них |
70 | { |
71 | button_state++; |
72 | if (button_state>1) {button_state=0;} |
73 | } |
74 | void ENCODER() //Считывание энкодера |
75 | { |
76 | encoder_B=digitalRead(4); |
77 | if (encoder_B==HIGH) {encoder_val--;} |
78 | if (encoder_B==LOW) {encoder_val++;} |
79 | } |
Перед проведением арифметической операции приведите исходную переменную к необходимому типу.
1
sech=(
long
)encoder_val*3600;
Замечу, что для "временнЫх" значений использование знаковых переменных наврядли стоит считать хорошей идеей.
1
sech=(
long
)encoder_val*3600;
Замечу, что для "временнЫх" значений использование знаковых переменных наврядли стоит считать хорошей идеей.
Одно из выражений к long. Или как выше или
1
sech=encoder_val*3600L;
Огромное спасибо! Вечером обязательно применю (работа, чтоб ее...).
А какие переменные (не знаковые) стоит использовать? Я предполагал, что таймер будет просто вычитать из общего исходного (установленного) количества секунд по единичке, вычленять из этого часы, минуты и секнды и индицировать их на семисегментнике. Не больше, не меньше. В этой ситуации (полагаю с высоты своей недообразованности!) нет разницы: все равно работать с положительными числами.
А почему в коде написано "long", а в тексте сообщения - "LONG"?
Меня все эти long, int, short итд ужасно раздражают. Каждый раз приходится вспоминать какого же они размера в данной архитектура.
Пользуйтесь лучше, int8_t, uint8_t, int16_t и так далее. Сразу все понятно.
Я в них не очень путаюсь: какой-никакой опыт с бэйсиком есть, но за совет спасибо, учту. Просто в данном случае было непонятно, в чем капкан. В VB6 такого не бывает: лонг - это лонг, добл - добл... И никаких дополнительных уточнений в тексте делать не надо. А тут....
Для уточнения, в каком месте затыка. Если нарушил правила форума - прошу простить: новичок я...
Си дрессирует программиста по полной программе. Приготовьтесь к этому.
ну дак это С, брат. У тебя справа от = два выражения, оба типа int, компилятор их и складывает как int, при этом int переполняется и уходит в область отрицательных значений. Слева от = переменная типа long, поэтому результат расширяется до long с сохранением знака. Чтоб такого не было, одно из выражений справа надо явно привести к типу long, тогда второе приведется к нему же отоматисски и результат будет правильный.
А вот вопрос, кстати: это правило распространяется на правое выражение любой сложности или же только на два операнда?
Т.е. валидно ли выражение
1
sech=((
long
)encoder_val_01 * 3600) + (encoder_val_02 * 3600);
или encoder_val_02 необходимо тоже к long приводить? Могу ли я на буквочках исходника сыканомить или лучше не дразнить gcc-ей?
Столь далеко мое скудоумие не простираеца, надо спросить ЕвгенийПетровича.
Но памойму, достаточно чонить одно привести явно, остальные подтянуца
надо спросить ЕвгенийПетровича.
однозначно спросить надо.
Однако ставлю на то, что приводить надо. Я бы в своем коде приводил оба.
Вы меня троллите?
Понятно, что преобразование делается только по мере необходимости, а значит в выражении (encoder_val_02 * 3600) ничего преобразовываться не будет. Преобразуется только его результат.
Да и проверить это нетрудно:
01
#include <Printing.h>
02
03
void
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
11
void
loop
(
void
) {}
12
//
13
// Результат
14
//
15
// s1=84928
16
// s2=216000
17
//
о, я выиграл :)
ну дак это С, брат. У тебя справа от = два выражения, оба типа int, компилятор их и складывает как int, при этом int переполняется и уходит в область отрицательных значений. Слева от = переменная типа long, поэтому результат расширяется до long с сохранением знака. Чтоб такого не было, одно из выражений справа надо явно привести к типу long, тогда второе приведется к нему же отоматисски и результат будет правильный.
Большое спасибо! Теперь все разжевано, в будущем буду учитывать! Вот, занялся на седьмом десятке, уж вся грудь седая, а все как мальчик :)
Дорогие форумчане, всем большое спасибо. Вопрос, как я понимаю, снят. Тему, наверное, можно завершить, чтоб не флудить. Но это на Вашще усмотрение.
о, я выиграл :)
Снимаю шляпу.
Снимаю шляпу.
С кого? :)
просто повезло...
Снимаю шляпу.
С кого? :)
А дида то наш, не прост, успел в далекой юности с прохожих, мандатровые, посдергивать. Вот и прет привычка.))))
Снимаю шляпу.
С кого? :)
А дида то наш, не прост, успел в далекой юности с прохожих, мандатровые, посдергивать. Вот и прет привычка.))))
тогда б я так и написал. "Сдергиваю"
Но я всего лишь почтительно снимаю свою. Перед Великими.
А дида то наш, не прост
«П-прекратите д-демагогию! — взорвался наконец и Фёдор Симеонович. — К-как вам не с-совестно нести такую чушь? К-какой я вам п-простой человек? И что это за словечко такое — п-простой? Это д-дубли у нас простые!»
(надеюсь, ичточник не надо указывать? :))
[/quote]
(надеюсь, ичточник не надо указывать? :))
[/quote]
Конечно, не надо. Спасибо, повеяло моей юностью.... :)
Друзья, всем спасибо!
"sech=encoder_val*3600L;" мне показалось короче, нежели "
sech=(
long
)encoder_val*3600;", применил первый вариант (сэкономил объем кода). Всё сразу же забегало, заскакало, замигало, заработало :)
Век живи, век учись у умных и образованных людей!
L можно незаметить, а вот (long) уже не пропустишь. Да и не всегда в вычислениях есть константы, к которым можно прилепить L, UL. На объёме результирующего кода это не сказывается. Так что это - дело вкуса.
L можно незаметить, а вот (long) уже не пропустишь. Да и не всегда в вычислениях есть константы, к которым можно прилепить L, UL. На объёме результирующего кода это не сказывается. Так что это - дело вкуса.
Ок, тонкости усёк, буду иметь ввиду.
Си дрессирует программиста по полной программе. Приготовьтесь к этому.