Date encoding through Righ Shift >> and Left Shift << bitwise operation

Viewed 46

I am trying to solve a C programming problem, which is date encoding in 2 bytes using bit shifting operations and decoding back to date, month and year. While I could also use bitwise & and | operation, I want to use just bit shifting.

The issue is: even though everything seems to be right the output of date and month has no effect of dual bit shifting operation. The year is okay because there is only one operation. Meaning that the dual operation of bit shift is somehow not behaving as required.

I have used unsigned char so last sign bit is definitely not the issue. The Showbits function is just to show the bits. I am using gcc g++ compiler with VSCode in Windows 10.

//  Date encoding in 2-byte number

#include <stdio.h>
#include <conio.h>

typedef unsigned int uint;

Showbits(int n)
{
    int i, k, mask;

    for (i = 15; i >= 0; i--)
    {
        mask = 1 << i;
        k = n & mask;
        k == 0 ? printf("0") : printf("1");
    }
}

int main()
{

    uint encd_date, date, month, year;

    year = 2022;
    month = 9;
    date = 15;
    encd_date = 512 * (year - 1980) + 32 * month + date;

    printf("\nEncoded date:%u\n", encd_date);
    printf("\nencd_date: ");
    Showbits(encd_date);

    year = (1980 + (encd_date >> 9));
    month = (encd_date << 7);
    month = (month >> 12);
    date = (encd_date << 11);
    date = (date >> 11);

    printf("\ndate: ");
    Showbits(date);

    printf("\nmonth: ");
    Showbits(month);

    printf("\nyear: ");
    Showbits(year);

    printf("\nDecoded date %u month %u year %u", date, month, year);

    return 0;
}
2 Answers

I change the data type suitable for 16-bit as suggested by Fe2O3 and it works as expected.

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

typedef uint16_t uint;

void Showbits(int n)
{
    int i, k, mask;

    for (i = 15; i >= 0; i--)
    {
        mask = 1 << i;
        k = n & mask;
        k == 0 ? printf("0") : printf("1");
    }
}

int main()
{

    uint encd_date, date, month, year;

    year = 2022;
    month = 9;
    date = 15;
    encd_date = 512 * (year - 1980) + 32 * month + date;

    printf("\nEncoded date:%u\n", encd_date);
    printf("\nencd_date: ");
    Showbits(encd_date);

    year = (1980 + (encd_date >> 9));
    month = (encd_date << 7);
    month = (month >> 12);
    date = (encd_date << 11);
    date = (date >> 11);

    printf("\ndate: ");
    Showbits(date);

    printf("\nmonth: ");
    Showbits(month);

    printf("\nyear: ");
    Showbits(year);

    printf("\nDecoded date %u month %u year %u", date, month, year);

    return 0;
}

It's seems the carry-out of the left shift is being dragged back in on the right shift operations.

A bridging 'fix' is to use an intermediary variable that seems to "clear up" the problem. This seems to work(around) for whatever reason.

int main() {
    uint16_t y = 2022;
    uint16_t m = 9;
    uint16_t d = 15;
    uint16_t u;

    uint16_t encd_date = (y-1980)*512 + m*32 + d;

                            y = (encd_date >> 9) + 1980;
    u = encd_date <<  7;    m = u >> (7+5);
    u = encd_date << 11;    d = u >> 11;

    printf( "\nDecoded year %d month %d date %d", y, m, d );

    return 0;
}

/* Output
Decoded year 2022 month 9 date 15
*/

EDIT Coming back to this... The OP sought to use << (with >>) to clear high-order bits. Here is an alternative that does not left-shift bits into oblivion. Instead, 3 smallish values are put into an unsigned 16 bit buffer, then extracted again, using shift and +/- (which is effectively like bitmasking).

And an alternative version of Showbits() for a 16 bit value

void Showbits( uint16_t n ) {
    uint16_t cpy = n;
    char buf[ 16 ] = { '0' };
    for( int i = sizeof buf; i-- && cpy; cpy >>= 1 )
        buf[ i ] = "01"[ cpy & 1 ];
    printf( "\n%04X = %.16s\n", n, buf );
}

int main() {
    uint16_t yb = 1900; // 7 bit range: 1900-2027

    uint16_t d = 15, m = 9, y = 2022; // 15 Sept 2022

    // Initial values
    printf( "\n:::: == day %02d month %02d year %d (%d+%d)", d, m, y, yb, y-yb );

    // squeeze into 16 bits: 7+4+5 : yyyyyyymmmmddddd
    uint16_t enc = ((y-yb<<4)+m<<5) + d;
    Showbits( enc );

    // output directly
    // demonstrates right to left processing of function call parameters
    // y is assigned before used to calc m and
    // m is assigned before used to calc d
    printf( "\n%04X == day %02d month %02d year %d",
        enc, (enc-(y<<9))-(m<<5), m=(enc-(y<<9))>>5, yb+(y=(enc>>9)) );

    // or, first decompose
    y =  enc >> 9;
    m = (enc - (y << 9)) >> 5;
    d = (enc - (y << 9)) - (m<<5);
    y += yb;

    // output values
    printf( "\n%04X == day %02d month %02d year %d", enc, d, m, y );

    return 0;
}

Output

:::: == day 15 month 09 year 2022 (1900+122)
F52F = 1111010100101111

F52F == day 15 month 09 year 2022
F52F == day 15 month 09 year 2022
Related