为什么TimeSpan.FromSeconds(double)舍入到毫秒?

时间:2011-01-12 18:16:43

标签: .net timespan base-class-library

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

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

有人能说出为什么这种看似适得其反的行为被实施了吗?

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

5 个答案:

答案 0 :(得分:11)

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

  

<强>参数

     

value 类型:System.Double

     

几秒钟,精确到最接近的毫秒

原因可能是因为双倍并不准确。在比较双打时进行一些舍入总是一个好主意,因为它可能只是比你期望的更大或更小。当你试图输入整整毫秒时,这种行为实际上可以为你提供一些意想不到的纳秒。我认为这就是他们选择将值四舍五入到整数毫秒并丢弃较小数字的原因。

答案 1 :(得分:7)

关于投机的权利..

  1. TimeSpan.MaxValue.TotalMilliseconds等于922337203685477.该数字为15位数。
  2. double精确到15位。
  3. TimeSpan.FromSecondsTimeSpan.FromMinutes等都会转换为double中表示的毫秒数(然后转到当前不感兴趣的TimeSpan
  4. 因此,当您创建接近TimeSpan(或TimeSpan.MaxValue)的MinValue时,转换将精确到毫秒。\ br /> 因此可能回答问题“为什么”是:始终具有相同的精度。<登记/> 需要考虑的另一件事是,如果通过首先将值转换为long中表示的刻度来完成转换,是否可以更好地完成工作。

答案 2 :(得分:4)

想象一下,您是负责设计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。即使您了解为什么会发生这种情况(将xy加在一起的精确度降低),您必须承认从客户角度来看,似乎有点奇怪。然后他把这个扔给你:

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值构造的,以产生结果,其精度超过{{1}的精度}键入自己 -eg,100000000000000.5(16位有效数字) - 或者你知道,允许这样做。

所以你决定,你知道什么,我会做到这一点,以便任何使用double来构造double的方法将四舍五入到最接近的毫秒。这样,明确记录了,从TimeSpan转换为double是一项有损操作,在客户从转换后看到奇怪的行为的情况下解雇我TimeSpandouble并希望获得准确的结果。

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

答案 3 :(得分:1)

我认为有解释:TimeSpan structure incorrectly handles values close to min and max value

看起来它不会很快改变: - )

答案 4 :(得分:0)

FromSeconds使用私有方法Interval

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

0x3e8 == 1000

该const上的Interval方法多重播放值,然后强制转换为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)个标志的精确度。 使用反射器调查