Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# signed/unsigned cast issues

Tags:

c#

I need to convert between signed integers and their internal representation as series of bytes. In C I was using functions like:

unsigned char hibyte(unsigned short i)
{return i>>8;}

unsigned char lobyte(unsigned short i)
{return i & 0xFF;}

unsigned short makeshort(unsigned char hb, unsigned char lb)
{return ((short)hb << 8) | (short)lb;}

The problem is that this code does not work under C# because the rules of signed/unsigned cast are not the same: as I understand a C# cast mean conversion of the value whereas in C casting between signed/unsigned types does not modify the underlying data. Moreover in C#, for signed numbers the >> operator shifts in the sign bit. All this makes it difficult to convert my code to C# e.g.

1) the C# function

public static byte hibyte(short i)
{return (byte) (i>>8);}

throws an overflow exception if i is negative

2) the C# function

public static ushort makeshort(byte hb, byte lb)
{return (short) (((ushort)hb << 8) | (ushort)lb); }

throws an overflow exception if the resulting short is negative. Here the expression "(ushort)hb << 8" works because the shift is done on unsigned number. But then I need to interpret the same data as a signed integer and I don't know how to do it. I understand for C# such C-like cast is cheating because a positive value may become a negative value but this is what I actually need (eg. for processing a byte stream read from a device etc.) For the moment I'm using the C code compiled as an unmanaged dll for all binary manipulations like this but this is not very elegant and I'm sure this can be done somehow (possibly simply) in C#. Any suggestions are welcome!

like image 671
user5493558 Avatar asked Oct 18 '25 16:10

user5493558


2 Answers

Several answers have already noted the BitConverter class, as well as using unchecked with bit shifts and casts. I'll just quickly demonstrate the third option: "C-style union structs".

[StructLayout(LayoutKind.Explicit)]
struct Converter
{
   [FieldOffset(0)]
   public ushort UshortValue;
   [FieldOffset(0)]
   public short ShortValue;
   [FieldOffset(0)]
   public byte LoByte;    
   [FieldOffset(1)]
   public byte HiByte;
}

Then use like so.

ushort test1 = new Converter { ShortValue = -123 }.UshortValue; // 65413
ushort test2 = new Converter { HiByte = 1, LoByte = 100 }.UshortValue; // 356
byte test3 = new Converter { UshortValue = 356 }.LoByte;  // 100

It has the advantage over BitConverter that you don't need to allocate a temporary byte array.

like image 130
Anders Forsgren Avatar answered Oct 20 '25 06:10

Anders Forsgren


You could use the BitConverter class to do this instead:

short x = 1;
byte[] bytes = BitConverter.GetBytes(x);
short y = BitConverter.ToInt32(bytes, 0);

This has overloads for the other integral types int and long too.

If you really want to write the code yourself, you can avoid overflow exceptions by specifying unchecked like so:

public static byte hibyte(short i)
{
    unchecked
    {
        return (byte)(i >> 8);
    }
}

public static ushort makeushort(byte hb, byte lb)
{
    unchecked
    {
        return (ushort)((hb << 8) | lb);
    }
}

public static short makeshort(byte hb, byte lb)
{
    unchecked
    {
        return (short)((hb << 8) | lb);
    }
}

I'd just use BitConverter though; it's pretty fast. However, note that it always uses the endianness of the machine on which the code is running.

This is reported via BitConverter.IsLittleEndian.

If the data you're converting has a different endianness you have to do it yourself.

like image 44
Matthew Watson Avatar answered Oct 20 '25 04:10

Matthew Watson



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!