36

TimeSpan.FromSeconds需要一个双精度值,并且可以表示低至 100 纳秒的值,但是这种方法莫名其妙地将时间四舍五入到整毫秒。

鉴于我刚刚花了半个小时来查明这种(记录在案的!)行为,知道为什么会出现这种情况会更容易忍受浪费的时间。

谁能提出为什么实施这种看似适得其反的行为?

TimeSpan.FromSeconds(0.12345678).TotalSeconds
    // 0.123
TimeSpan.FromTicks((long)(TimeSpan.TicksPerSecond * 0.12345678)).TotalSeconds
    // 0.1234567
4

5 回答 5

13

正如您自己发现的那样,这是一个记录在案的功能。它在TimeSpan 的文档中有所描述:

参数

类型:System.Double

秒数,精确到最接近的毫秒

造成这种情况的原因可能是因为 double 根本不准确。在比较双打时进行一些四舍五入总是一个好主意,因为它可能只是比您预期的大一点或小一点。当您尝试输入整毫秒时,这种行为实际上可能会为您提供一些意想不到的纳秒。我认为这就是他们选择将值四舍五入到整毫秒并丢弃较小数字的原因。

于 2011-01-21T19:44:36.957 回答
7

关于投机权..

  1. TimeSpan.MaxValue.TotalMilliseconds等于 922337203685477。有 15 位数字的数字。
  2. double精确到 15 位。
  3. TimeSpan.FromSeconds等都TimeSpan.FromMinutes经过转换为以毫秒表示的毫秒double(然后转为滴答声,然后转为TimeSpan现在不感兴趣的)

因此,当您创建TimeSpan将接近TimeSpan.MaxValue(或MinValue)时,转换将仅精确到毫秒
因此,“为什么”这个问题的可能答案是:始终保持相同的精度。 进一步要考虑的是,如果转换是通过首先将值转换为以.
long

于 2011-01-21T14:50:38.670 回答
5

假设您是负责设计TimeSpan类型的开发人员。您已经具备了所有基本功能;这一切似乎都很好。然后有一天,某个 Beta 测试人员出现并向您展示了以下代码:

double x = 100000000000000;
double y = 0.5;
TimeSpan t1 = TimeSpan.FromMilliseconds(x + y);
TimeSpan t2 = TimeSpan.FromMilliseconds(x) + TimeSpan.FromMilliseconds(y);
Console.WriteLine(t1 == t2);

为什么会输出False测试人员问你。即使您理解为什么会发生这种情况(将x和加在一起时精度损失),您也必须承认,从客户的角度来看,y这确实有点奇怪。然后他把这个扔给你:

x = 10.0;
y = 0.5;
t1 = TimeSpan.FromMilliseconds(x + y);
t2 = TimeSpan.FromMilliseconds(x) + TimeSpan.FromMilliseconds(y);
Console.WriteLine(t1 == t2);

那个输出True测试人员的怀疑是可以理解的。

此时,您需要做出决定。您可以允许TimeSpan在从值构造的值之间进行算术运算double以产生精度超过double类型本身精度的结果——例如,100000000000000.5(16 个有效数字)——或者你可以,你知道,不允许这样做。

所以你决定,你知道吗,我会这样做,以便任何使用 adouble构造 a 的方法TimeSpan都将四舍五入到最接近的毫秒。这样,明确记录了从 a 转换double为 a是一个有损操作,如果客户在从toTimeSpan转换后看到这样的奇怪行为并希望得到准确的结果,则可以免除我的责任。doubleTimeSpan

我不一定认为这是“正确”的决定。显然,这种方法本身会引起一些混乱。我只是说需要以一种或另一种方式做出决定,而这显然是决定的。

于 2011-01-22T07:43:35.760 回答
1

I think the explanation is there: TimeSpan structure incorrectly handles values close to min and max value

And it looks like it's not going to change any time soon :-)

于 2011-01-20T20:57:00.553 回答
0

FromSeconds 使用私有方法 Interval

public static TimeSpan FromSeconds(double value)
{
    return Interval(value, 0x3e8);
}

0x3e8 == 1000

该 const 上的 Interval 方法 multiplay 值,然后转换为 long (见最后一行):

private static TimeSpan Interval(double value, int scale)
{
    if (double.IsNaN(value))
    {
        throw new ArgumentException(Environment.GetResourceString("Arg_CannotBeNaN"));
    }
    double num = value * scale; // Multiply!!!!
    double num2 = num + ((value >= 0.0) ? 0.5 : -0.5);
    if ((num2 > 922337203685477) || (num2 < -922337203685477))
    {
        throw new OverflowException(Environment.GetResourceString("Overflow_TimeSpanTooLong"));
    }
    return new TimeSpan(((long) num2) * 0x2710L); // Cast to long!!!
}

结果,我们有 3 个 (x1000) 符号的精度。 使用反射器进行调查

于 2011-01-21T05:11:05.830 回答