Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Сдвиг и маска  (Прочитано 15624 раз)
0 Пользователей и 1 Гость смотрят эту тему.
IgnisFatuus
Постоялец

ru
Offline Offline

« : 20-07-2015 17:51 » 

Товарищи, подскажите пожалуйста где у меня косяк?

Имеется 16 бит числа. Биты  с 15 по 11 - это часы от 0 до 23, биты 10-5 это минуты, биты 4-0 это секунды.



Код: (C)
//Шаг первый - делаю некие действия, получаю число
int time = 41805

//в битовом представлении это
1010 0011 0100 1101


//мне нужны биты  1010 0 ...
//я делаю
int hours = ((time &  0xF800 ) >> 11);

//результат 19...
//но 10100 - это же 20!!!!

//Дубль 2 - делаю некие действия, получаю число
int time = 42280

//в битовом представлении это
1010 0101 0010 1000

//мне нужны биты  1010 0 ...
//я делаю

int hours = ((time &  0xF800 ) >> 11);
//результат 20...
//Как так???
 

Записан
Finch
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #1 : 20-07-2015 18:17 » 

Код: (C++)
#include <stdio.h>

int main() {
        //Шаг первый - делаю некие действия, получаю число
int time = 41805;

//в битовом представлении это
//1010 0011 0100 1101


//мне нужны биты  1010 0 ...
//я делаю
int hours = ((time &  0xF800 ) >> 11);


//результат 19...
//но 10100 - это же 20!!!!
        printf("Result 1 = %d\n", hours);

//Дубль 2 - делаю некие действия, получаю число
time = 42280;

//в битовом представлении это
//1010 0101 0010 1000

//мне нужны биты  1010 0 ...
//я делаю

hours = ((time &  0xF800 ) >> 11);
//результат 20...
//Как так???
        printf("Result 2 = %d\n", hours);
        return 0;
}
$ g++ ignis.cpp -o ignis
$ ./ignis
Result 1 = 20
Result 2 = 20
$
Что я делаю не так?
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
IgnisFatuus
Постоялец

ru
Offline Offline

« Ответ #2 : 20-07-2015 20:02 » 

Хм... А сдвиг от платформы зависит? А то у меня это наблюдалось на Android, там ARM. Вы, как я понимаю, это проверяли под x86 ?
Записан
Finch
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #3 : 20-07-2015 20:08 » 

Можно сделать unsigned int time; Eсли тип int 16 разрядный. знаковый бит может вводить поправки. Хотя не думаю. Нужно читать доку по ARM как там реализованы сдвиги.
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
darkelf
Молодой специалист

no
Offline Offline

« Ответ #4 : 21-07-2015 05:37 » 

Судя по полученному значению похоже, что это действительно возникает из-за использования int, вместо unsigned int.
Кстати посмотрел стандарт - параграф 6.5.7 пункт 5:
Цитата
The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1/2(^E2). If E1 has a signed type and a negative value, the resulting value is implementation-defined.
« Последнее редактирование: 21-07-2015 06:17 от darkelf » Записан
IgnisFatuus
Постоялец

ru
Offline Offline

« Ответ #5 : 21-07-2015 10:54 » 

Помогло, спасибо. Однако, видимо, я что - то еще упускаю.Дело в том, что я занимаюсь изучением библиотеки FatFS и  там- то эта проблема и возникла - дата и время там хранятся в своем формате, есть double word переменная 16 бит даты и 16 бит со временем.

Код: (C++)
bit31:25
Year origin from the 1980 (0..127)
bit24:21
Month (1..12)
bit20:16
Day of the month(1..31)
bit15:11
Hour (0..23)
bit10:5
Minute (0..59)
bit4:0
Second / 2 (0..29)

Накидал тут простенькую программку, чтобы глянуть - а правильно будет время обрабатываться потом - вот что получилось :

Код: (C++)
#include <iostream>
#include <string.h>
typedef unsigned long    DWORD;
typedef unsigned short    WORD;
int main() {
    time_t t = time(NULL);
    struct tm tm_ = *localtime(&t);
    WORD date;
    WORD time;
    DWORD variable = ((DWORD)(tm_.tm_year - 80) << 25)
                   | (((DWORD)tm_.tm_mon + 1) << 21)
                   | ((DWORD)tm_.tm_mday << 16)
                   | ((DWORD)tm_.tm_hour << 11)
                   | ((DWORD)tm_.tm_min << 5)
                   | ((DWORD)((tm_.tm_sec >> 1)));
    memcpy(&time, &variable, sizeof(time));
    date = (variable>> 16);
    std::cout << "time = " << time << std::endl;
    std::cout << "date = " << date << std::endl;
    tm last_mod;
    last_mod.tm_year = (((date & 0xFE00) >> 9) + 80);
    last_mod.tm_mon = ((date & 0x1E0) >> 5);
    if(last_mod.tm_mon != 0) last_mod.tm_mon -=1;
    last_mod.tm_mday = ((date & 0x1F));
    int result_s = (((int)time & (int)0xF800) >> 11);
    last_mod.tm_hour = (unsigned)result_s;
    last_mod.tm_min = ((time & 0x7E0) >> 5);
    last_mod.tm_sec = ((time & 0x1F));
    std::cout << "create TS   " << mktime(&tm_) << std::endl;
    std::cout << "restored TS " <<   mktime(&last_mod) << std::endl;
    return 0;
}


На x86 вывод -
time = 27725
date = 18165
create TS    1437474866
restored TS 1437471253

хотя я проверил все цифры (дата и время, год, месяц) полностью совпадают..
Записан
darkelf
Молодой специалист

no
Offline Offline

« Ответ #6 : 21-07-2015 11:46 » 

Вы при обратном пересчёте забыли секунды умножить на 2.

Код: (C)
#include <time.h>
#include <iostream>
#include <string.h>
typedef unsigned long    DWORD;
typedef unsigned short    WORD;
int main(void) {
    time_t t = time(NULL);
    struct tm tm_ = *gmtime(&t);
    WORD wdate;
    WORD wtime;
    DWORD variable = ((DWORD)(tm_.tm_year - 80) << 25)
                   | ((DWORD)(tm_.tm_mon + 1) << 21)
                   | ((DWORD)tm_.tm_mday << 16)
                   | ((DWORD)tm_.tm_hour << 11)
                   | ((DWORD)tm_.tm_min << 5)
                   | ((DWORD)((tm_.tm_sec >> 1)));
    printf("variable = %u (%x)\n", variable, variable);
    wtime = (variable & 0xffff);
    wdate = (variable >> 16);
    printf("time = %u (%x)\n", wtime, wtime);
    printf("date = %u (%x)\n", wdate, wdate);

    struct tm last_mod = { 0 };
    last_mod.tm_year = (((wdate >> 9) & 0x7F) + 80);
    last_mod.tm_mon = (((wdate >> 5) & 0xF) - 1);
    last_mod.tm_mday = ((wdate & 0x1F));
    last_mod.tm_hour = ((wtime >> 11) & 0x1F);
    last_mod.tm_min = ((wtime  >> 5) & 0x3F);
    last_mod.tm_sec = ((wtime & 0x1F) << 1);

    t = mktime(&tm_);
    printf("create TS %u (%x)\n", t, t);
    t = mktime(&last_mod);
    printf("create TS %u (%x)\n", t, t);
    return 0;
}
« Последнее редактирование: 21-07-2015 11:50 от darkelf » Записан
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #7 : 21-07-2015 11:47 » 

Пара вопросов по ходу:

1. Как у этой модели ARM обстоит дело с endianness?
2. Не проще ли вместо чехарды со сдвигами и масками использовать bit fields?
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
IgnisFatuus
Постоялец

ru
Offline Offline

« Ответ #8 : 21-07-2015 12:10 » 

Огромное спасибо, все раработало.

Стало быть надо было еще поменять очередность сдвига и наложения маски?
Записан
darkelf
Молодой специалист

no
Offline Offline

« Ответ #9 : 21-07-2015 12:22 » new

Наверное не надо, у меня просто такой стиль кода. Если Вы в своём варианте добавите умножение секунд на 2:
Код: (C++)
-     last_mod.tm_sec = ((time & 0x1F));
+     last_mod.tm_sec = ((time & 0x1F) << 1);
 
, то заработает и Ваш вариант.
Записан
IgnisFatuus
Постоялец

ru
Offline Offline

« Ответ #10 : 21-07-2015 12:31 » 

Проблема была еще и в "прогреве буфера" ...

Код: (C++)
//у меня
tm last_mod;

//у Вас...
tm last_mod = {0};

Еще раз огромное спасибо!
Записан
darkelf
Молодой специалист

no
Offline Offline

« Ответ #11 : 21-07-2015 12:50 » 

ну, не совсем "прогрев" - на самом деле, там есть признак учёта летнего/зимнего времени  tm_isdst , который, может "неожиданно" влиять на результат. Лучше инициализировать структуры заполняемые по-частям, что-бы потом не иметь непонятных эффектов.

Ну и ещё у меня там gmtime(), опять-же что-бы временная зона не влияла на результат.
« Последнее редактирование: 21-07-2015 12:52 от darkelf » Записан
IgnisFatuus
Постоялец

ru
Offline Offline

« Ответ #12 : 21-07-2015 12:55 » 

Работает и с моим localtime(), только надо как раз прогреть и секунды умножить.
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #13 : 29-07-2015 21:32 » 

Цитата
Код: (C)
typedef unsigned long    DWORD;
typedef unsigned short    WORD;

Другой проц и все сломается. Есть такой stdint.h — не пробовал? int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t.
И полезно включать больше варнингов при компиляции: смешанные знаково‑бесзнаковые операции могут быть источником неожиданных проблем.
« Последнее редактирование: 29-07-2015 21:34 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines