c#有符号/无符号强制转换问题
本文关键字:转换 问题 无符号 符号 | 更新日期: 2023-09-27 18:19:22
我需要在有符号整数和它们的内部表示之间转换为一系列字节。在C语言中,我使用如下函数:
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;}
问题是,这段代码不能在c#下工作,因为有符号/无符号类型转换的规则是不一样的:据我所知,c#类型转换意味着值的转换,而在C中,有符号/无符号类型之间的类型转换不会修改底层数据。此外,在c#中,对于有符号数,>>运算符在符号位移位。所有这些使得将我的代码转换为c#变得困难,例如
1) c#函数public static byte hibyte(short i)
{return (byte) (i>>8);}
如果I为负,抛出溢出异常
2) c#函数public static ushort makeshort(byte hb, byte lb)
{return (short) (((ushort)hb << 8) | (ushort)lb); }
如果结果short为负,抛出溢出异常。在这里(短)hb <<因为移位是在无符号数上进行的。但是我需要将相同的数据解释为有符号整数,我不知道怎么做。我知道对于c#来说,这种类似C的强制转换是作弊,因为正值可能会变成负值,但这正是我真正需要的。用于处理从设备读取的字节流等)目前,我正在使用C代码编译为所有二进制操作的非托管dll,但这不是很优雅,我确信这可以在c#中以某种方式完成(可能简单)。欢迎提出任何建议!
几个答案已经注意到BitConverter
类,以及使用unchecked
进行位移位和强制转换。我将快速演示第三个选项:" c风格的联合结构"。
[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;
}
然后使用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
与BitConverter
相比,它的优点是不需要分配临时字节数组
您可以使用BitConverter
类来代替:
short x = 1;
byte[] bytes = BitConverter.GetBytes(x);
short y = BitConverter.ToInt32(bytes, 0);
这对其他整型int
和long
也有重载。
如果你真的想自己写代码,你可以通过像这样指定unchecked
来避免溢出异常:
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);
}
}
我只使用BitConverter
;它非常快。但是,请注意,它总是使用运行代码的机器的端序。
这是通过BitConverter.IsLittleEndian
报告的。
如果你要转换的数据有不同的端序,你必须自己做
正如其他人所提到的,您可以使用BitConverter
类,尽管它们无法解释实际代码中的端序(仅在最后简要提到):
public static (byte Hibyte, byte Lobyte) GetBytes(short i)
{ // This is my recommendation; it gets both bytes in one call, so it
// may be more efficient.
var bytes = BitConverter.GetBytes(i);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes)
return (bytes[0], bytes[1]);
}
public static (byte Hibyte, byte Lobyte) GetBytes(ushort i)
{ // BitConverter works equally well with unsigned types.
var bytes = BitConverter.GetBytes(i);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes)
return (bytes[0], bytes[1]);
}
public static byte Hibyte(short i)
{ // If you want to use your original schema, here's the hi byte:
if (BitConverter.IsLittleEndian)
return BitConverter.GetBytes()[1];
return BitConverter.GetBytes()[0];
}
public static byte Hibyte(ushort i)
{ // Again, same thing works for ushort
if (BitConverter.IsLittleEndian)
return BitConverter.GetBytes()[1];
return BitConverter.GetBytes()[0];
}
public static short MakeShort(byte hb, byte lb)
{
byte[] bytes = new byte[] { hb, lb };
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return BitConverter.ToInt16(bytes);
}
public static ushort MakeUShort(byte hb, byte lb)
{
byte[] bytes = new byte[] { hb, lb };
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return BitConverter.ToUInt16(bytes);
}
而且,虽然其他人提到使用unchecked
,但他们忽略了一个问题:正如您在原始问题中指出的那样,在有符号整数类型上进行右移时,符号位重复,因此,虽然这有效:
public static byte Hibyte(ushort i)
{ return (byte)(i >> 8); }
// No need for unchecked, because result will always fit in one byte and is never < 0.
public static byte Lobyte(ushort i)
{ return (byte)(i & 0xFF); } // No need for unchecked for the same reason.
public static byte LoByte(short i)
{ return (byte)(i & 0xFF); }
// Like above, no need for unchecked for the same reasons; also, bitwise & works the same
// for both signed and unsigned types.
public static ushort MakeUShort(byte hb, byte lb)
{ return (ushort)((hb << 8) | lb); }
// Again, no need for unchecked; result is always 16 bits and never negative.
public static short MakeShort(byte hb, byte lb)
{ unchecked { return (short)((hb << 8) | lb); } }
// This time, we may need unchecked because result may overflow short.
…符号 short
的高字节的代码需要额外的强制转换:
public static byte Hibyte(short i)
{ unchecked { return (byte)((ushort)i >> 8); } }
// Again, the unchecked is needed, this time because i may be negative,
// which may need to be accounted for when casting to a ushort.
通过在移位前将有符号的short
转换为无符号的ushort
,我们避免了符号位被加到前面8次。或者,我们可以使用按位&
来忽略传播的符号位:
public static byte HiByte (short i)
{ return (byte)((i >> 8) & 0xFF); }
// Since bitwise operations never result in overflow, and by the time we cast at the end,
// the number is guaranteed to fit in a byte and be >= 0, we no longer need any unchecked blocks.