Search code examples
cgccbit-manipulationbitwise-operatorsbit-shift

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


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;
}

Solution

  • 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