如何无精度损失地在DateTime与double之间往返转换?
无精度损失在DateTime与double之间往返转换的方案
先明确:咱们不是在讨论「把DateTime序列化为double再反序列化」这种设计合不合理——毕竟你已经说了必须这么做,那咱们直接解决问题。
现有方案的精度问题
首先说大家可能最先想到的两种方法,它们都存在精度损失:
方法1:使用
DateTime.ToOADate()
这是很多资料里提到的常规做法,但精度损失非常严重。比如运行这段F#代码:let now = DateTime.UtcNow in DateTime.FromOADate(now.ToOADate()).Ticks - now.Ticks结果通常是类似
val it : int64 = -7307L的数值,相当于近1毫秒的误差,对于对时间精度要求高的场景完全不可用。方法2:直接在Ticks(long)和double之间转换
这种方式精度稍好,但依然不精确。运行这段代码:let now = DateTime.UtcNow in DateTime(int64(float(now.Ticks))).Ticks - now.Ticks结果大概是
val it : int64 = -42L,虽然误差缩小了,但还是存在精度丢失——原因在于double的有效位数只有53位,而DateTime的Ticks是64位整数,直接做数值转换时会丢失低位信息。
无精度损失的解决方案
要实现完全无精度损失的往返转换,核心是不要做数值转换,而是直接转换二进制位模式。.NET里的System.BitConverter.Int64BitsToDouble()和System.BitConverter.DoubleToInt64Bits()正好能实现这个需求:
Int64BitsToDouble():把64位整数(也就是DateTime的Ticks)的二进制位原样映射到double的64位存储空间中DoubleToInt64Bits():反过来,把double的二进制位原样还原为64位整数,再转成DateTime
对应的F#代码示例:
// DateTime 转 double(无精度损失) let dateTimeToDouble (dt: DateTime) = System.BitConverter.Int64BitsToDouble dt.Ticks // double 转 DateTime(无精度损失) let doubleToDateTime (d: double) = DateTime(System.BitConverter.DoubleToInt64Bits d)
注意事项
- 这个方案仅适用于long(DateTime.Ticks的类型)和double之间的位模式转换,本质是把double当成一个64位的存储容器来用,而非利用它的数值特性
- 要注意字节序的问题:这两个方法依赖主机的字节序,所以如果是在同一台机器上往返转换完全没问题;如果需要跨不同字节序的机器传输,你需要额外处理字节序的统一
内容的提问来源于stack exchange,提问作者mt99
相关产品推荐
相关产品推荐

