Search code examples
cbitsigned

Converting from smaller data type to bigger and vice versa


I have this C code which tries to store a signed short in signed char. signed char range is [-128,127] and signed short range is [-32768, 32767].

signed char sChar = SCHAR_MAX;
printf("Signed Char: %d", sChar);

signed short sShort = SHRT_MAX;
printf("\nSigned Short: %d", sShort);

sChar = sShort;
printf("\nSigned Short to Signed Char: %d", sChar);

Output:

Signed Char: 127
Signed Short: 32767
Signed Short to Signed Char: -1

This what I think happens in background.

signed short in bits: 0111       1111   1111  1111 (32767)
signed char in bits: <discarded bits>   1111  1111 (-1)

Basically it copies all bits from right and discards the remaining. TL;DR It truncates short to store it in char thus we lose information.

Question starts here. Information above was to give background.

Lets say I give signed char -1 and try to store it in signed short. The value inside signed short will be -1. In binary if represent it would be:

signed char in bits:              1111  1111 (-1)
signed short in bits: 1111  1111  1111  1111 (32767)

My question is how does compiler assigns char to short in background? It is definitely not one to one bit copy like above. My guess is that it shifts the bits to the right and ones' compliment the remaining.

          1111 1111 // Signed Char
1111 1111 0000 0000 // Shifting bits
1111 1111 1111 1111 // Ones' complimenting the right 8 bits 

Solution

  • This isn't a complete and good answer because i want to sleep, But i think may help to you.

    I will edit it soon.

    Code

    #include <stdio.h>
    #include <limits.h>
    
    #show bits
    //https://stackoverflow.com/questions/111928/is-there-a-printf-converter-to-print-in-binary-format
    
    void printbitsc( signed char x)
    {
        for(int i=sizeof(x)<<3; i; i--)
            putchar('0'+((x>>(i-1))&1));
    }
    
    void printbitss( signed short x)
    {
        for(int i=sizeof(x)<<3; i; i--)
            putchar('0'+((x>>(i-1))&1));
    }
    
    //https://www.geeksforgeeks.org/little-and-big-endian-mystery/
    
    /* function to show bytes in memory, from location start to start+n*/
    void show_mem_rep(char *start, int n)  
    { 
        int i; 
        for (i = 0; i < n; i++) 
             printf(" %.2x", start[i]);
         
        printf("\n");
     
        if (start[0]==0x67)
            printf("little endian\n"); 
        else
            printf("big endian\n"); 
        
    } 
    
    int main(int argc, char const *argv[]) {
    
        int i = 0x01234567; 
        show_mem_rep((char *)&i, sizeof(i)); 
        
        printf("%zu\n",sizeof (signed short));
        printf("%zu\n",sizeof (signed char));
    
        signed char sChar = SCHAR_MAX;
        printf("Signed Char: %d\n", sChar);
        printbitsc(sChar);
    
        signed short sShort = SHRT_MAX;
        printf("\nSigned Short: %d\n", sShort);
        printbitss(sShort);
    
        sChar = sShort;
    
        printf("\nSigned Short to Signed Char: %d\n", sChar);
        printbitsc(sChar);
      
    
        signed char ssChar = SCHAR_MAX;
        printf("Signed Char: %d\n", ssChar);
        printbitsc(ssChar);
    
        signed short ssShort = SHRT_MAX;
        printf("\nSigned Short: %d\n", ssShort);
        printbitss(ssShort);
    
        ssShort=ssChar;
    
        printf("\nSigned Short to Signed Char: %d\n", ssShort);
        printbitsc(ssShort);
    
    
        return 0;
    }
    

    In little endian data store inside memory on reverse order, i.e. 0x01: 1 value store in memory: 0x10:1111_0000:240 (change order of one and zero)

    So in first case we face with a truncation case as

    ss: 0111_1111_1111_1111 7 F F F

    Inside memory we actually have: F F F 7

    and for

    sc: 0111_1111 7 F

    In memory we actually have: F 7

    Now by assignment first element of ss memory i.e. FF will truncate and placed on 16 bit sc place.

    F F -> F 7

    Now we actually have in memory: 1111_1111

    lets translate it to human readable number (i convert from twos complement to decimal)

    first of all fist one means minus (1: -) now we reach to :111_1111 by tracking conversion algorithm : minus [ not( 111_1111) + 1] = minus[(000_0000)+1] = minus [1] = -1

    At last we reach to -1 on sc variable.

    In second case integer promotion will accrued.

    sc: 0111_1111 7 F

    Memory: F 7

    ss: 0111_1111_1111_1111 7 F F F

    Memory: F F F 7

    We work with memory F7 wants to go inside FFF7 so F7 must be promoted to a 16 bits length by adding excess 0.

    These zeroes added after first element and F7 changes to F700.

    Now the question is how F700 go to 16 bit length place.

    F700 -> F F F 7

    After that we face to F700 on a 16 bits length variable.

    As we know F700 actually represents these bit pattern: 0000_0000_0111_1111 when we have read it with normal printf(), its show 007F.

    By simple binary to decimal conversion 007F equals to 127.

    ToDo

    • Edit post to make it better