-1

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;
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Have you tried specifying `uint16_t` so that there's a "left margin" for those high-order bits to fall off when left shifted? – Fe2O3 Sep 16 '22 at 11:01
  • Why are you using g++ for compiling C? That doesn't make any sense. `Showbits(int n)` is nonsense in both languages, this won't compile. – Lundin Sep 16 '22 at 11:05
  • After some more search, I came across the "Integer Promotion" and I found that is the real issue. Internally unsigned int is 4 bytes so I need to change the code accordingly. – Rakesh Solanki Sep 16 '22 at 11:05
  • You say "I have used `unsigned char`" but there is no trace of it in your code... Please clarify 1) why you are using a C++ compiler for C code 2) How you even managed to get this code with `Showbits` compile in any programming language 3) Which integer types you are actually using. – Lundin Sep 16 '22 at 11:11
  • @Fe2O3 You got it right but uint16_t is not showing in gcc – Rakesh Solanki Sep 16 '22 at 11:14
  • https://stackoverflow.com/q/17436527/17592432 – Fe2O3 Sep 16 '22 at 11:17
  • @Lundin 1. Check this line: typedef unsigned int uint; 2. I didn't noticed g++ is C++ compiler, I will change 3. I could complile Showbits function, what is the issue in the function? 4. I am using unsigned int data type for main program. Thanks. – Rakesh Solanki Sep 16 '22 at 11:20
  • Functions in the C and C++ languages must return a type, that's what's wrong. You probably meant `void Showbits(int n)` – Lundin Sep 16 '22 at 11:35
  • @Lundin You are right I missed void return in the function definition. But still, it complies and that is not the issue. – Rakesh Solanki Sep 16 '22 at 12:41
  • Is there a practical reason you can't use `&` and `|`, or is this an intellectual exercise? (Using `&` and `|` certainly would have been easier!) – Steve Summit Sep 16 '22 at 22:52

2 Answers2

1

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

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
Fe2O3
  • 6,077
  • 2
  • 4
  • 20