0

Let's assume I have a signed integer on my system and it is stored as little endian.

int32_t x = -456;

How do I make a function that converts this to big endian so I can store it in a file with different signedness from my system?

This is my attempt:

int32_t big_endian(int32_t x) {
    int sign = (x < 0) ? -1 : 1;
    uint32_t y = (uint32_t)x;
    
    // flip bits of y
    
    return (int32_t)y * sign;
}

I am concerned about that leftmost bit that determines the signedness, because I feel that converting to unsigned, and then flipping, will return the signedness bit to the wrong byte.

basedchad21
  • 121
  • 7
  • 2
    https://stackoverflow.com/questions/105252/how-do-i-convert-between-big-endian-and-little-endian-values-in-c – Sven Nilsson Apr 16 '23 at 15:55
  • Did you test your function? Anyway, it doesn't swap anything. – Jabberwocky Apr 16 '23 at 16:02
  • 1
    [edit] and show a [mcve] with test data and expected output. – Jabberwocky Apr 16 '23 at 16:04
  • See also here: https://stackoverflow.com/questions/19275955/convert-little-endian-to-big-endian. – wohlstad Apr 16 '23 at 16:15
  • @Jabberwocky there is literally test data in the first code segment, and the expected output in the second. Also, if I knew how to write the function, I wouldn't need to ask a question. The function I wrote was supposed to serve solely as an ADDITON in order to better explain what I want. Do you want me to include stdint for you, or what exactly do you expect me to additionally write or clarify? – basedchad21 Apr 16 '23 at 16:18
  • @others. I was asking about how to handle SIGNED value conversion. Not how to convert a uint32_t like every other example does. There are no examples with SIGNED values – basedchad21 Apr 16 '23 at 16:21
  • 2
    There is no need to handle the sign. Simply swap the first and last byte, and then the second and third one! – Sven Nilsson Apr 16 '23 at 16:36
  • Endianness is about the order in which the value's bits are grouped in bytes. It does not matter whether those bits represent a signed or unsigned value. – paddy Apr 16 '23 at 16:41
  • 1
    @paddy totally wrong. – n. m. could be an AI Apr 16 '23 at 16:53
  • 3
    A C question is not a duplicate of a C++ question. – Eric Postpischil Apr 16 '23 at 16:56
  • Recommended reading [The byte order fallacy](https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html). – n. m. could be an AI Apr 16 '23 at 17:08
  • @n.m. his code seems shorter because he totally omits the part how he got the value into a nice array of bytes so it can be so easily handled. Owait, each one is a separate read instruction from the stream? isn't that slower than just getting 4 bytes? – basedchad21 Apr 16 '23 at 17:18
  • "how he got the value into a nice array of bytes" Read an array of bytes and construct your data out of it. That's one call to `read` or `fread` or whatever. "each one is a separate read instruction from the stream?" What makes you think so? Even if it was the case, streams are normally buffered so even calling `fgetc` four times has negligible cost compared to the actual I/O. – n. m. could be an AI Apr 16 '23 at 17:31
  • @n.m. Wrong how exactly? I'm saying nothing special needs to be done apart from reversing the byte order. Except obviously bitwise operations should be done on an unsigned value, but that's not what I was getting at because the OP clearly demonstrates an understanding of that in their code already. Their only confusion was about whether the sign needs special handling, which it does not. – paddy Apr 17 '23 at 00:06
  • @paddy Endianness is not about the order of bits in bytes (which is not defined unless your bits are individually addressable), it is about the order of bytes in words. – n. m. could be an AI Apr 17 '23 at 03:33
  • @n.m. I think my phrasing was ambiguous then. I did not intend to imply the order of individual bits is different. I meant the order of groups of bits (in bytes), trying to emphasize that the actual _bits_ whether representing a signed or unsigned value have nothing to do with endianness. Obviously I failed be concise. Whatever. – paddy Apr 17 '23 at 08:10
  • ah ok, it indeed wasn't clear. – n. m. could be an AI Apr 17 '23 at 10:18

2 Answers2

2

Calculating the bytes that represent an int32_t can be a slight nuisance due to the C standard not fully defining shifts with negative values. However, since the exact-width signed integer types are defined to use two’s complement, their representations are the same as for the corresponding unsigned integer type obtained by converting the signed type to the unsigned type. So we can convert to uint32_t and shift to obtain the desired bytes, as the following code demonstrates.

#include <stdlib.h>
#include <limits.h>


/*  Convert a big-endian object to a little-endian object or vice-versa,
    in place.
*/
void ReverseEndian(void *p, size_t n)
{
    //  Convert to "unsigned char *" for convenience.
    unsigned char *pu = p;

    /*  Swap bytes at the front of the object with reflected bytes from the
        back.
     */
    for (size_t i = 0; i < n/2; ++i)
    {
        unsigned char t = pu[i];
        pu[i] = pu[n-1-i];
        pu[n-i-1] = t;
    }
}


//  Store an int32_t as big-endian.
void StoreBigEndian(void *p, int32_t x)
{
    //  Convert to "unsigned char *" for convenience.
    unsigned char *pu = p;

    /*  Convert to uint32_t.  Since int32_t is defined to use two’s complement,
        uint32_t is represented with the sign bytes, and it avoids the sign
        issues.
    */
    uint32_t u = x;

    /*  Shift each byte, starting with the least significant, to move it to
        the low byte, then store those bytes in big-endian order, starting
        with the highest addressed.
    */
    for (size_t i = 0; i < sizeof u; ++i)
        pu[sizeof u - 1 - i] = u >> i*CHAR_BIT;
}


//  Store an int32_t as little-endian.
void StoreLittleEndian(void *p, int32_t x)
{
    //  Convert to "unsigned char *" for convenience.
    unsigned char *pu = p;

    /*  Convert to uint32_t.  Since int32_t is defined to use two’s complement,
        uint32_t is represented with the sign bytes, and it avoids the sign
        issues.
    */
    uint32_t u = x;

    /*  Shift each byte, starting with the least significant, to move it to
        the low byte, then store those bytes in big-endian order, starting
        with the lowest addressed.
    */
    for (size_t i = 0; i < sizeof u; ++i)
        pu[i] = u >> i*CHAR_BIT;
}


#include <stdio.h>


//  Print n bytes starting at address p.
static void PrintBytes(const void *p, size_t n)
{
    //  Convert to "unsigned char *" for convenience.
    const unsigned char *pu = p;

    printf("Hexadecimal");
    for (size_t i = 0; i < n; ++i)
        printf(" %0*x", (CHAR_BIT+3)/4, pu[i]);
}


#include <string.h>


int main(void)
{
    int32_t i = 0x12345678;

    //  Buffers for big-endian and little-endian representations.
    unsigned char BE[sizeof i];
    unsigned char LE[sizeof i];

    //  Store the value as big endian and display it.
    StoreBigEndian(BE, i);
    printf("Big Endian:  "); PrintBytes(BE, sizeof BE); printf(".\n");

    //  Store the value as little endian and display it.
    StoreLittleEndian(LE, i);
    printf("Little Endian:  "); PrintBytes(LE, sizeof LE); printf(".\n");

    //  Copy the bytes into new int32_t objects to reinterpret them.
    int32_t ibe, ile;
    memcpy(&ibe, BE, sizeof ibe);
    memcpy(&ile, LE, sizeof ile);

    if (ibe == i)
        printf("This system appears to be big endian.\n");
    else if (ile == i)
        printf("This system appears to be little endian.\n");
    else
        printf("This system appears to be neither big endian nor little endian.\n");

    //  Reverse the endianness and test for problems.
    ReverseEndian(BE, sizeof BE);

    if (memcmp(BE, LE, sizeof BE))
    {
        fprintf(stderr, "Error, big-endian bytes are inconsistent with little-endian bytes.\n");
        exit(EXIT_FAILURE);
    }
}
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
1

Don't reinvent the wheel when you need to convert your host's data to big-endian format. For 32-bit integer values, just use htonl():

SYNOPSIS

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

DESCRIPTION

These functions shall convert 16-bit and 32-bit quantities between network byte order and host byte order.

On some implementations, these functions are defined as macros.

The uint32_t and uint16_t types are defined in <inttypes.h>.

RETURN VALUE

The htonl() and htons() functions shall return the argument value converted from host to network byte order.

It's also available on Windows.

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56