【问题标题】:How Accurate is DateTime.AddDays?DateTime.AddDays 的准确性如何?
【发布时间】:2009-04-20 16:14:33
【问题描述】:

由于 DateTime.AddDays() 采用double 参数,我担心当您添加天数时,可能会出现一些舍入错误。例如,假设我有以下循环:

DateTime Now = DateTime.Today;
for (int i = 0; i < 365; ++i)
{
    Now = Now.AddDays(1);
    // do something
}

我担心现在可能会开始偏离午夜。我总是很想做这样的事情,这可能会慢一点,但可以缓解我的偏执:

for (int i = 0; i < 365; ++i)
{
    Now = Now.AddDays(1.01).Date;
    // do something
}

【问题讨论】:

  • 如果你用Now = 23:59调用你提议的方法,会导致错误的结果。

标签: c# datetime rounding


【解决方案1】:

DateTime 将日期内部存储为 64 位整数,其中一个刻度表示 100 纳秒,因此没有出错的风险。一天有 864,000,000,000 个刻度,Double 的精度至少为 15 位。因此,当舍入到刻度时,每个错误都会消失,因为如果 1.0 等于一天,Double 的分辨率高于一个刻度。

这不适用于AddYears(),因为如果1.0 等于一年,Double 的精度不足以表示一个刻度。但是,如果您查看 DateTime 类,您会发现设计尊重这一事实 - AddMonths()AddYears() 具有整数参数而不是浮点参数。

要检查这一点,只需执行以下代码。

DateTime now = DateTime.Now;

// Displays 864000000000 
Console.WriteLine(now.AddDays(1.0).Ticks - now.Ticks);

【讨论】:

    【解决方案2】:

    一般来说,我认为您担心使用双精度数舍入是正确的,因为并非所有实数都绝对可以用双精度数表示 - 例如,如果您将一天的 1/3 相加 3 次,您可能不会最后正好提前一天。但是,在这种情况下,1 是一个绝对可以表达的数字,因为您只是将它乘以另一个也绝对可以用双精度表达的数字(一天中的刻度数),您应该没问题。第二个样本可能有点矫枉过正。

    例子:

    DateTime now = DateTime.Today;
    for (int i = 0; i < 7; ++i )
    {
        for (int j = 0; j < 7; ++j )
        {
             now = now.AddDays( 1 / 7.0 );
        }
    }
    Console.WriteLine( DateTime.Today);
    Console.WriteLine( now );
    

    结果(2009 年 4 月 20 日)

    4/20/2009 12:00:00 AM
    4/26/2009 11:59:59 PM
    

    【讨论】:

    • 也许您应该在示例中添加注释。 1 可以精确地用 Double 表示这一事实可能会得出这样的结论,即这对所有整数都是正确的,这当然是错误的。
    • 您必须增加很多天数才能成为问题。我怀疑你会首先溢出日期时间结构,因为双精度至少有 15 个有效数字。
    【解决方案3】:

    天数乘以一个整数(刻度),然后加上DateTime 存储的刻度数。当你传入一个整数时,你最终会添加一个整数的刻度。

    另一方面,我不知道 .NET 对闰秒做了什么...我怀疑它使用了一个非常简单的模型,不会打扰它们,让你的代码没问题。

    不要忘记添加时区会增加额外的复杂性 - 在本地添加一天可能会添加多于或少于 UTC 的一天,反之亦然。

    【讨论】:

      【解决方案4】:

      你说的远离午夜是什么意思?

      运行时,第一个代码的最后日期为 2010 年 4 月 20 日凌晨 12:00:00。
      我认为这就是你所期望的。不是吗?

      【讨论】:

        【解决方案5】:

        在您的示例中,我将 1 天添加到 DateTime 变量 100,000 次,但仍然以具有午夜时间值的日期结束。似乎没有理由担心。

        我确信他们正在四舍五入到时间刻度,这消除了漂移的任何问题。

        【讨论】:

          猜你喜欢
          • 2013-12-24
          • 2010-09-28
          • 2010-11-14
          • 1970-01-01
          • 2010-09-24
          • 1970-01-01
          • 1970-01-01
          • 2020-12-08
          相关资源
          最近更新 更多