在没有内存分配的情况下在 C# 中将 float/double/decimal 转换为 (u)int、(u)long
Converting float/double/decimal to (u)int, (u)long in C# without memory allocation
我正在为我的游戏开发 c# 网络解决方案,我在序列化 float
s、double
s 和 decimal
s 时遇到问题。
我的第一次尝试 - BitConverter
class。但是每次我使用 class 时,都会创建一个新的 byte[] 数组,这对于我的网络解决方案(例如 - 玩家位置同步)来说可能有点过头了。
总而言之,我的期望是:
- 可以在不分配内存的情况下将
float
s、double
s 和 decimal
s 转换为 (u)int、(u)long。
- 可以从服务器读取 (u)int 和 (u)long 并将其转换回原始类型(仍然无需分配额外的内存)。
编辑 1
这是我在 NetworkWriter 方法中用来存储要发送的字节的方法:
public void WriteByte(byte value)
{
Buffer[_position++] = value;
}
这些是我对 NetworkWriter 的扩展:
public static void WriteUInt(this NetworkWriter writer, uint value)
{
writer.WriteByte((byte) value);
writer.WriteByte((byte) (value >> 8));
writer.WriteByte((byte) (value >> 16));
writer.WriteByte((byte) (value >> 24));
}
我想将 4 字节浮点数转换为 4 字节 (u)int,这样我就可以将结果 int 传递给 Write(U)Int 序列化程序。
我还没有完成 NetworkReader,所以我不能告诉你确切的代码,但它会恢复 Write 方法。
我预计会有很多 float/double 操作,因为:
- Vector3(class 保持玩家位置)是一包 3 个花车,
- 延迟测量将send/receive加倍
- 四元数(class 保持玩家轮换)也是 4 个花车的包装。
这就是为什么我想尽可能减少 float/double/decimal 的内存分配。
编辑 2
重要说明:我知道什么时候需要 float、double 等,让我快速伪代码 reader 看起来像什么:
static float ReadFloat():
- Get 4 int bytes from buffer
- Construct float from those
很多选项:
BitConverter
有类似 Int32BitsToSingle(Int32)
的方法(从和到,32 位和 64 位),在基元之间进行按位强制转换;从那里你可以得到 unsigned 平凡
BinaryPrimitives
为许多在 span 上工作的原语提供了读写方法;跨度可以在堆栈上以进行零分配(或者更好:可以在您的输出缓冲区中)
- 你自己的
unsafe
代码;获取 a local/parameter/field/interior-pointer 的地址,并在取消引用之前强制指针
Unsafe
类型(尤其是 As
方法)允许来自 ref
的相同大小基元之间的直接强制转换(类似于 unsafe
选项,但是预先编写,并使用 IL 来避免非托管指针)
MemoryMarshal
类型(尤其是 Cast
方法)允许在 spans 类型上进行相同的直接转换,包括不同大小的类型之间;特别是,这允许您将任何不包含引用的原语转换为 Span<byte>
我正在为我的游戏开发 c# 网络解决方案,我在序列化 float
s、double
s 和 decimal
s 时遇到问题。
我的第一次尝试 - BitConverter
class。但是每次我使用 class 时,都会创建一个新的 byte[] 数组,这对于我的网络解决方案(例如 - 玩家位置同步)来说可能有点过头了。
总而言之,我的期望是:
- 可以在不分配内存的情况下将
float
s、double
s 和decimal
s 转换为 (u)int、(u)long。 - 可以从服务器读取 (u)int 和 (u)long 并将其转换回原始类型(仍然无需分配额外的内存)。
编辑 1
这是我在 NetworkWriter 方法中用来存储要发送的字节的方法:
public void WriteByte(byte value)
{
Buffer[_position++] = value;
}
这些是我对 NetworkWriter 的扩展:
public static void WriteUInt(this NetworkWriter writer, uint value)
{
writer.WriteByte((byte) value);
writer.WriteByte((byte) (value >> 8));
writer.WriteByte((byte) (value >> 16));
writer.WriteByte((byte) (value >> 24));
}
我想将 4 字节浮点数转换为 4 字节 (u)int,这样我就可以将结果 int 传递给 Write(U)Int 序列化程序。
我还没有完成 NetworkReader,所以我不能告诉你确切的代码,但它会恢复 Write 方法。
我预计会有很多 float/double 操作,因为:
- Vector3(class 保持玩家位置)是一包 3 个花车,
- 延迟测量将send/receive加倍
- 四元数(class 保持玩家轮换)也是 4 个花车的包装。
这就是为什么我想尽可能减少 float/double/decimal 的内存分配。
编辑 2
重要说明:我知道什么时候需要 float、double 等,让我快速伪代码 reader 看起来像什么:
static float ReadFloat():
- Get 4 int bytes from buffer
- Construct float from those
很多选项:
BitConverter
有类似Int32BitsToSingle(Int32)
的方法(从和到,32 位和 64 位),在基元之间进行按位强制转换;从那里你可以得到 unsigned 平凡BinaryPrimitives
为许多在 span 上工作的原语提供了读写方法;跨度可以在堆栈上以进行零分配(或者更好:可以在您的输出缓冲区中)- 你自己的
unsafe
代码;获取 a local/parameter/field/interior-pointer 的地址,并在取消引用之前强制指针 Unsafe
类型(尤其是As
方法)允许来自ref
的相同大小基元之间的直接强制转换(类似于unsafe
选项,但是预先编写,并使用 IL 来避免非托管指针)MemoryMarshal
类型(尤其是Cast
方法)允许在 spans 类型上进行相同的直接转换,包括不同大小的类型之间;特别是,这允许您将任何不包含引用的原语转换为Span<byte>