TimeSpan FromMilliseconds 奇怪的实现?

发布于 2024-10-26 10:28:27 字数 536 浏览 1 评论 0原文

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

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

FromMilliseconds 采用双精度作为参数。然而,该值似乎是内部四舍五入的。

如果我实例化一个具有 5000 个刻度 (0.5 毫秒) 的新 TimeSpan,则 TotalMilliseconds 的值是正确的。

查看反射器中的 TimeSpan 实现,可以发现输入实际上被转换为 long。

为什么 Microsoft 设计 FromMilliseconds 方法来采用双精度参数而不是 long(因为对于此实现,双精度值是无用的)?

I recently encountered some weird behaviour in the .NET TimeSpan implementation.

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

FromMilliseconds takes a double as parameter. However, it seems the value is rounded internally.

If I instantiate a new TimeSpan with 5000 ticks (.5 ms), the value of TotalMilliseconds is correct.

Looking at the TimeSpan implementation in reflector reveals that the input is in fact casted to a long.

Why did Microsoft design the FromMilliseconds method to take a double a parameter instead of a long (since a double value is useless given this implementation)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

软糯酥胸 2024-11-02 10:28:27

第一个考虑因素是想知道为什么他们选择 double 作为返回值。使用long将是一个显而易见的选择。虽然已经有了一个非常好的属性,那就是“长”,但 Ticks 的单位是 100 纳秒,这一点是明确的。但他们选择了 double,可能是为了返回小数值。

然而,这产生了一个新问题,这个问题可能直到后来才被发现。双精度数只能存储 15 个有效数字。一个TimeSpan可以存储10,000年。 非常希望从 TimeSpan 转换为毫秒,然后再转换回 TimeSpan 并获得相同的值。

这对于双打来说是不可能的。计算一下:10,000 年大约为 10000 x 365.4 x 24 x 3600 x 1000 = 315,705,600,000,000 毫秒。数出 15 位数字(最好是双精度数),您将得到恰好一毫秒作为仍然可以存储而没有舍入误差的最小单位。任何额外的数字都将是随机噪声。

设计人员(测试人员?)进退两难,在从 TimeSpan 转换为毫秒时,必须在对值进行四舍五入之间做出选择。或者稍后在从毫秒到时间跨度时执行此操作。他们选择尽早这样做,这是一个勇敢的决定。

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

The first consideration is wondering why they selected a double as the return value. Using long would have been an obvious choice. Although there already is a perfectly good property that is long, Ticks is unambiguous with a unit of 100 nanoseconds. But they picked double, probably with the intention to return a fractional value.

That however created a new problem, one that was possibly only discovered later. A double can store only 15 significant digits. A TimeSpan can store 10,000 years. It is very desirable to convert from TimeSpan to milliseconds, then back to TimeSpan and get the same value.

That isn't possible with a double. Doing the math: 10,000 years is roughly 10000 x 365.4 x 24 x 3600 x 1000 = 315,705,600,000,000 milliseconds. Count off 15 digits, best a double can do, and you get exactly one millisecond as the smallest unit that can still be stored without round-off error. Any extra digits will be random noise.

Stuck between a rock and a hard place, the designers (testers?) had to choose between rounding the value when converting from TimeSpan to milliseconds. Or to do it later when going from milliseconds to TimeSpan. They chose to do it early, a courageous decision.

Solve your problem by using the Ticks property and multiplying by 1E-4 to get milliseconds.

陪你搞怪i 2024-11-02 10:28:27

显然,这是设计使然。 文档说了这么多:

value参数转换为
刻度数,刻度数是
用于初始化新的 TimeSpan。
因此,价值只会是
被认为精确到最接近
毫秒。

This is by design, obviously. The documentation says as much:

The value parameter is converted to
ticks, and that number of ticks is
used to initialize the new TimeSpan.
Therefore, value will only be
considered accurate to the nearest
millisecond.

靑春怀旧 2024-11-02 10:28:27

接受双精度是一个合乎逻辑的设计。您可以有几分之一毫秒。

内部发生的是实现设计。即使(CLI 的)当前所有实现都首先围绕它,将来也不一定如此。

Accepting a double is a logical design. You can have fractions of milliseconds.

What's happening internally is an implementation design. Even if all current implementations (of the CLI) round it first that doesn't have to be the case in the future.

裂开嘴轻声笑有多痛 2024-11-02 10:28:27

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

value 参数被转换为刻度,并且该刻度数用于初始化新的 TimeSpan。因此,只会被认为精确到最接近的毫秒。

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

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

    <块引用>

    因此,只会被认为精确到最接近的毫秒 滴答声,或百万分之一秒

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

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

摘要:

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

The problem with your code is actually the first line, where you call FromMilliseconds. As noted previously, the remarks in the documentation state the following:

The value parameter is converted to ticks, and that number of ticks is used to initialize the new TimeSpan. Therefore, value will only be considered accurate to the nearest millisecond.

In reality, this statement is neither correct nor logically sound. In reverse order:

  • Ticks are defined as "one hundred nanoseconds". By this definition, the documentation should have been written as:

    Therefore, value will only be considered accurate to the nearest millisecond tick, or one ten-millionth of a second.

  • Due to a bug or oversight, the value parameter is not converted directly to ticks prior to initializing the new TimeSpan instance. This can be seen in the reference source for TimeSpan, where the millis value is rounded prior to its conversion to ticks, rather than after. If maximum precision were to be preserved, this line of code should have read as follows (and the adjustment by 0.5 milliseconds 3 lines earlier would be removed):

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

Summary:

The documentation for the various TimeSpan.From*, with the exception of FromTicks, should be updated to state that the argument is rounded to the nearest millisecond (without including the reference to ticks).

恍梦境° 2024-11-02 10:28:27

或者,您可以这样做:

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

--sarcasm

TimeSpan 将毫秒的两倍转换为刻度,因此“显然”您可以拥有小于 1ms 粒度的 TimeSpan。

-/讽刺

——这根本不明显......
为什么这不在 .FromMilliseconds 方法内完成,我无法理解。

Or, you could do:

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

--sarcasm

TimeSpan converts the double of milliseconds to ticks so "OBVIOUSLY" you can have a TimeSpan with less than a 1ms granularity.

-/sarcasm

-- this isn't obvious at all...
why this isn't done inside the .FromMilliseconds method is beyond me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文