странное значение математических операций
- Войдите на сайт для отправки комментариев
Ср, 23/05/2012 - 09:32
Всем привет. Наткнулся на странные грабли, уже голову сломал, может кто сталкивался или может объяснить почему вот такой простой скетч неправильно выполняет простые математические операции:
void setup(){ Serial.begin(9600); byte second, minute, hour; hour=12; minute=58; second=55; unsigned long secondCounter=hour*3600+minute*60+second; Serial.print("secondCounter "); Serial.println(secondCounter); } void loop(){ }
на консоль выводится secondCounter 4294948495
если же убрать в объявлении переменной long unsigned, то есть написать
long secondCounter=hour*3600+minute*60+second;
то выведет вообще отрицательное число: secondCounter -18801
Проблему решил, хотя почему компилятор так странно себя ведет все же не понял. Решается проблема добавлением префикса L к целочисленной константе 3600, т.е. запись должна выглядеть вот так:
unsigned long secondCounter=hour*3600L+minute*60+second;
иначе компилятор эти 3600 как-то хитро обрезает и получается неправильный результат. Это странно, т.к. в мануале написано, что "По умолчанию целочисленные константы относятся компилятором к типу int." и соответственно "int занимает 2 байта памяти, и может хранить числа от -32 768 до 32 767 (от -2^15 до 2^15-1)" а 3600 свободно попадает в этот диапазон.
P.S. странный эффект заметил, можно долго биться над проблемой, но после того как напишешь сообщение о проблеме на форуме часто очень быстро сам находишь решение, иногда даже оно находится на этапе описания проблемы.
А попробуйте вот так:
или так:
Еще проблема решается так
А попробуйте вот так:
У вас получается использование лишней памяти. Если точно известно какое максимальное значение должна принять переменная то зачем выделять ей больше. Естественно все выше сказаное касается случая когда каждый байт на счету. Хотя лучше сразу учится правильно писать.
long - да это лишнее.
А unsigned int это и есть uint16_t
А unsigned int это и есть uint16_t
а там вполне хватает 8-ми бит если до 255
Это странно, т.к. в мануале написано, что "По умолчанию целочисленные константы относятся компилятором к типу int." и соответственно "int занимает 2 байта памяти, и может хранить числа от -32 768 до 32 767 (от -2^15 до 2^15-1)" а 3600 свободно попадает в этот диапазон.
Ничего странного. Вычисления производятся с точностью, определяемой типом переменных/констант в правой части выражения (точнее - "самым точным" операндом). Затем происходит преобразование к типу переменной, стоящей в левой части.
Результат действий над переменными/константами типа int тоже относится к типу int, поэтому, умножив 3600 на 12, вы получите не 43 200 (0xA8C0 или 1010100011000000b), а 10432 (0x28C0 или 0010100011000000) и установленный флаг переполнения. Чтобы действия изначально производились над переменными нужного типа, необходимо в выражении разместить хотя бы одну переменную/константу соответствующего типа. Либо long int (присобачив L к константе или определив хотя бы одну из учавствующих в выраженнии переменных с типом long int), либо float (введя умножение на константу 1.0, но тут может появиться погрешность, связанная с ограничениями на длину мантиссы).
PS:
Хотя нет, возможно, что 12*3600 даст в результате -22335 без флага переполнения (если 3600 будет воспринято компилятором как значение типа int). В любом случае - результат вычисляется с точностью/разрядностью, соответствующей точности/разрядности самого точного/высокоразрядного операнда.
Если unsigned long secondCounter = hour*3600 то не хватит.
то не хватит.
по моему мы зря спорим, может и я не совсем вьехал но мне кажется:
uint8_t second, minute, hour; - 12,58,55
uint16_t hourSec, minuteSec; - 3600, 60. Тут даже minuteSec можно к 8-ми и будет:
uint8_t second, minute, hour, minuteSec; - 12,58,55,60
uint16_t hourSec; - 3600
uint32_t secondCounter; - 46735 Если бы hour в даном случае не превышал 12 (тоесть от 0 до 12 а не 0 - 24) то и даную переменную можно смело отнести к uint16_t. а uint32_t ввести если нужно было бы посчитать общее количество секунд (46735 + 46735 и тд)
Вот, вполне работоспособно
Ничего странного. Вычисления производятся с точностью, определяемой типом переменных/констант в правой части выражения (точнее - "самым точным" операндом). Затем происходит преобразование к типу переменной, стоящей в левой части.
Теперь понятно, спасибо.