TimeSpan FromMilliseconds奇怪的实现?

我最近在.net timespan实现中遇到了一些奇怪的行为。

TimeSpan test = TimeSpan.FromMilliseconds(0.5); double ms = test.TotalMilliseconds; // Returns 0 

FromMilliseconds采用double作为参数。 但是,似乎内部的值是四舍五入的。

如果我使用5000个滴答(.5毫秒)实例化新的时间跨度,则TotalMilliseconds的值是正确的。

查看reflection器中的TimeSpan实现会发现输入实际上已经转换为long。

为什么Microsoft将FromMilliseconds方法设计为使用double参数而不是long(因为在实现时,double值是无用的)?

首先考虑的是他们为什么选择双倍作为回报值。 使用long将是一个明显的选择。 虽然已经有一个很长的完美的属性,但Ticks明确无误,单位为100纳秒。 但他们选择了两倍,可能是为了返回一个小数值。

然而,这产生了一个新问题,一个可能只是稍后才发现的问题。 双倍只能存储15位有效数字。 TimeSpan可存储1万年。 非常希望从TimeSpan转换为毫秒,然后再转换回TimeSpan并获得相同的值。

这是不可能的双倍。 算一算:10000年约为10000 x 365.4 x 24 x 3600 x 1000 = 315,705,600,000,000毫秒。 计算15位数,最好是双倍数,你得到的最小单位只有一毫秒,仍然可以存储而没有舍入误差。 任何额外的数字都是随机噪音。

设计师(测试人员?)被困在岩石和坚硬的地方之间,在从TimeSpan转换为毫秒时必须选择舍入值。 或者稍后从毫秒到TimeSpan。 他们选择早做,勇敢的决定。

通过使用Ticks属性并乘以1E-4来获得毫秒来解决您的问题。

显然,这是设计的。 文档说了很多:

value参数将转换为ticks,并且该ticks数用于初始化新TimeSpan。 因此,仅将值视为精确到最接近的毫秒。

接受双重是一种逻辑设计。 你可以有几分之一毫秒。

内部发生的是实现设计。 即使所有(CLI的)当前实现都是先进行的,但未来也不一定如此。

您的代码问题实际上是第一行,您调用FromMilliseconds 。 如前所述,文档中的备注说明如下:

value参数将转换为ticks,并且该ticks数用于初始化新TimeSpan。 因此,仅将视为精确到最接近的毫秒。

实际上,这种说法既不正确也不合逻辑。 按相反顺序:

  • 刻度被定义为“一百纳秒”。 根据这个定义,文档应该写成:

    因此, 价值只会被认为是最接近的 毫秒 打勾,或者一万分之一秒

  • 由于错误或疏忽,在初始化新TimeSpan实例之前, value参数不会直接转换为ticks。 这可以在TimeSpan的参考源中看到,其中millis转换为ticks 之前被舍入,而不是之后。 如果要保留最大精度,则此行代码应如下所示(并且将删除之前3行的0.5毫秒调整):

     return new TimeSpan((long)(millis * TicksPerMillisecond)); 

摘要:

应更新各种TimeSpan.From*的文档( FromTicks ,以声明参数舍入到最接近的毫秒(不包括对刻度的引用)。

或者,你可以这样做:

 double x = 0.4; TimeSpan t = TimeSpan.FromTicks((long)(TimeSpan.TicksPerMillisecond * x)); // where x can be a double double ms = t.TotalMilliseconds; //return 0.4 

– 讽刺

TimeSpan将两倍的毫秒转换为滴答,因此“ 显然 ”可以使TimeSpan的粒度小于1毫秒。

-/讽刺

– 这根本不明显……为什么在.FromMilliseconds方法中没有这样做是超出我的。