TimeSpan 和双舍入误差

发布于 2024-09-11 10:49:10 字数 1253 浏览 11 评论 0原文

我正在处理物理实体,例如时间、速度和距离,并执行简单的数学运算,例如距离 = 时间 * 速度等。速度和距离是四舍五入到第 8 位数字的双精度值,对于时间值,是 .NET TimeSpan被使用。由于 TimeSpan 四舍五入到最接近的毫秒,我遇到了舍入错误,因此我需要编写自定义舍入方法,将所有计算四舍五入到最接近的毫秒。例如(为简单起见,舍入到第 8 位数字被省略):

  static void Main(string[] args) {
     var dist = 1.123451;
     var speed = 1.123452;

     var timeA = TimeSpan.FromHours(dist / speed);
     var timeB = timeA + TimeSpan.FromMilliseconds(1);

     var distA = _round(timeA.TotalHours * speed);
     var distB = _round(timeB.TotalHours * speed);

     var timeA1 = TimeSpan.FromHours(distA / speed);
     var timeB1 = TimeSpan.FromHours(distB / speed);

     // a correct implementation should give both the following vars true
     var isDistributive = distA == dist;
     var isPrecise = (timeB1 - timeA1) == TimeSpan.FromMilliseconds(1);
  }

  public static double _round(double d) {
     // Q: what should be here?
  }
  • 使用 Math.Round(d, 6) 是分配性的,但会损失精度(精确到约 4 毫秒)
  • 使用 Math.Round(d, 7) 精确到单个毫秒,但不是分配性的(上面的 distA 将是 1.1234511 != 1.123451)
  • 使用以下内容(舍入到最接近的毫秒)似乎是正确的,但舍入代码本身会引入其自己的双精度错误:

     public static double _round(double d) {
        var pre = 3600000.0;
        返回 Math.Round(d * pre) / pre;
      }
    

谢谢, 鲍里斯。

I'm dealing with physical entities, such as time, speed and distance, and performing simple math, such as distance = time * speed, etc. Speed and Distance are double values rounded to 8th digit, and for Time values a .NET TimeSpan is used. Since TimeSpan rounds to a closest millisecond, I'm getting rounding errors, so I need to write custom rounding method that rounds all calculations to the closest millisecond. For example (rounding to 8th digit is omitted for simplicity):

  static void Main(string[] args) {
     var dist = 1.123451;
     var speed = 1.123452;

     var timeA = TimeSpan.FromHours(dist / speed);
     var timeB = timeA + TimeSpan.FromMilliseconds(1);

     var distA = _round(timeA.TotalHours * speed);
     var distB = _round(timeB.TotalHours * speed);

     var timeA1 = TimeSpan.FromHours(distA / speed);
     var timeB1 = TimeSpan.FromHours(distB / speed);

     // a correct implementation should give both the following vars true
     var isDistributive = distA == dist;
     var isPrecise = (timeB1 - timeA1) == TimeSpan.FromMilliseconds(1);
  }

  public static double _round(double d) {
     // Q: what should be here?
  }
  • Using Math.Round(d, 6) is distributive, but loses precision (precise up to ~4 msec)
  • Using Math.Round(d, 7) is precise to a single msec, but not distributive (distA above will be 1.1234511 != 1.123451)
  • Using the following (round to closest msec) seems to be correct, but the rounding code itself introduces its own double precision errors:

      public static double _round(double d) {
        var pre = 3600000.0;
        return Math.Round(d * pre) / pre;
      }
    

Thanks,
Boris.

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

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

发布评论

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

评论(2

心在旅行 2024-09-18 10:49:10

Jon Skeet 同样对 .Net 时间计算不满意,并致力于一个名为 noda-time,我相信这可以解决这个问题。我不知道该项目是否已经进展到足以对您有用,但值得检查。

编辑:无耻地引用 JS 的名字,希望让人们使用和改进库,而不是(不必要地)重新发明轮子。

Jon Skeet is likewise unhappy with .Net time calculations, and working on a project called noda-time, which I believe might solve this problem. I don't know if the project is far enough along to be of use to you yet, but it is worth checking.

Edit: Shamelessly invoking the name of JS in the hopes of getting people to use and improve libraries rather than (unnecessarily) reinventing the wheel.

梦断已成空 2024-09-18 10:49:10

尝试使用十进制而不是双精度类型。它们更精确(28 位精度),应该可以满足您的需求,而无需实现自定义舍入函数。

Try using decimal instead of double types. They are more precise (28 digits precision) and should fit your need without having to implement the custom round function.

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