【问题标题】:Why should I use t1 - t0 < 0, not t1 < t0, when using System.nanoTime() in Java为什么在 Java 中使用 System.nanoTime() 时应该使用 t1 - t0 < 0,而不是 t1 < t0
【发布时间】:2013-08-24 01:34:45
【问题描述】:

当我阅读 Java 中的 System.nanoTime() API 时。我找到了这一行:

应该使用 t1 - t0

http://docs.oracle.com/javase/7/docs/api/java/lang/System.html#nanoTime()

比较两个 nanoTime 值

long t0 = System.nanoTime();
...
long t1 = System.nanoTime();

应该使用 t1 - t0

我想知道为什么t1 - t0 &lt; 0 是防止溢出的更好方法。

因为我从其他一些帖子中了解到A &lt; BA - B &lt; 0 更可取。

Java Integer compareTo() - why use comparison vs. subtraction?

这两件事是矛盾的。

【问题讨论】:

    标签: java math comparison overflow


    【解决方案1】:

    纳秒时间不是“实时”时间,它只是一个计数器,当某些未指定的事件发生时(可能计算机已启动),它会从某个未指定的数字开始递增。

    它会溢出,并在某个时候变成负数。如果您的t0 刚好在它溢出之前(即非常大的正数),而您的t1 就在它之后(非常大的负数),那么t1 &lt; t0(即您的条件是错误的,因为t1 发生在@987654325 之后@).....

    但是,如果你说t1 - t0 &lt; 0,那么神奇的是,由于同样的上溢(undeflow)原因(非常大的负数减去非常大的正数会下溢),结果将是 t1 的纳秒数在t0.....之后是正确的。

    在这种情况下,两个错误确实是一个正确的!

    【讨论】:

    • 你输入了t1 - t0 &gt; 0,但不应该是t1 - t0 0
    • @MichaelWarner - 嗯....是的,你是对的.....t1 - t0 &lt; 0t1 &lt; t0的逻辑相同,除了t1 - t0 &lt; 0不会“遭受”纳秒环绕问题。现在编辑。不知道为什么我搞砸了。
    • 我假设Long.compareUnsigned(long, long) 做同样的事情,或者甚至可能更好地处理一些边缘情况。
    • @Trejkaz 不在此上下文中。它做了完全不同的事情,根本不返回两个 long 之间的差异。
    • 有没有一个很好的例子来说明它不同的情况?这个概念是相同的 - 它将负值移动到正值之上。实际上,我读的文档越多,他们读的越矛盾。相同方法的文档说您应该准备初始值已经是负数。如果一开始就已经是负数,回过来也能负数,那整件事情就等于没用了,不是吗?
    【解决方案2】:

    t0 - t1 &lt; 0 优于 t0 &lt; t1 当我们确定 real 值的差异(溢出前)不大于包含所有可能的值。
    对于纳秒,它将大约是 292 年(纳秒存储在 long 中,long 的一半大小是 2^64/2 = 2^63 纳秒 ~= 292 年)。

    所以对于间隔少于 292 年的时间样本,我们应该使用t0 - t1 &lt; 0 来获得正确的结果。


    为了更好地可视化,可以说循环包含 8 个可能的值,它们是 -4, -3, -2, -1 ,0, 1, 2, 3

    所以时间线可以看起来像

    real time values:  .., -6, -5, -4, -3, -2, -1,  0,  1,  2,  3,  4,  5,  6,  7, ..
    overflowed values: ..,  2,  3, -4, -3, -2, -1,  0,  1,  2,  3, -4, -3, -2, -1, ..
    

    让我们看看t0 - t1 &lt; 0t0 &lt; t1 对于差异将大于和不会大于 4 的值(循环大小的一半,-4 是最小值,这意味着它可以是计算的最小结果三角洲)。请注意,当t1 溢出时,只有t0 - t1 &lt; 0 会给出正确的结果

    1. delta = 1 overflow 具有较大的值(注意:我们不会使较小的值溢出,因为这意味着两个值都在同一个周期,所以计算就像没有溢出一样)

      • 实际值:t0 = 3t1 = 4
      • 溢出:t0 = 3t1 = -4
      • t0 &lt; t1 ==> 3 &lt; -4 ->
      • t0 - t1 &lt; 0 ==> 3 - (-4) &lt; 0 ==> -1 &lt; 0(7 溢出到 -1)true

      所以只有t0 - t1 &lt; 0 我们得到了正确的结果尽管 或者可能感谢 溢出。

    2. delta = 1 但这次没有溢出

      a) 正值

      • t0 = 2, t1 = 3
      • 2 &lt; 3 是的
      • 2 - 3 &lt; 0 ==> -1 &lt; 0

      b) 负值

      • t0 = -4, t1 = -3
      • -4 &lt; -3 是的
      • -4 - (-3) &lt; 0 ==> -1 &lt; 0

      对于其他实际 delta = 1 的情况,我们还将获得 t0 &lt; t1t0 - t1 &lt; 0 测试的正确结果(t0 - t1 将始终为 -1

    3. delta = 3(几乎是周期的一半)

      a1) 溢出具有更大的价值

      • 实际值:t0 = 3t1 = 6
      • 溢出:t0 = 3t1 = -2
      • t0 &lt; t1 ==> 3 &lt; -2 ->
      • t0 - t1 &lt; 0 ==> 3 - (-2) &lt; 0 ==> -3 &lt; 0 (5 溢出到 -3) true

      a2) 溢出

      的另一种情况
      • 实际值:t0 = 2t1 = 5
      • 溢出:t0 = 2t1 = -3
      • t0 &lt; t1 ==> 2 &lt; -3 ->
      • t0 - t1 &lt; 0 ==> 2 - (-3) &lt; 0 ==> -3 &lt; 0(再次溢出 5 次到 -3)true


      所以再次只有t0 - t1 &lt; 0 给出了正确的结果。

      b) 无溢出 t0 - t1 将始终等于 -3 (-delta),因此这将始终给出正确的结果。 t0 &lt; t1 也会给出正确的resilt

      • 实际值:t0 = -1t1 = 2
      • t0 &lt; t1 ==> -1 &lt; 2 ->
      • t0 - t1 &lt; 0 ==> -1 - 2 &lt; 0 ==> -3 &lt; 0
    4. t0 - t1

      delta = 4 结果将始终等于 -4,因此它也将是 &lt;0

      示例溢出
      a1)

      • 实际值:t0 = 0t1 = 4
      • 溢出:t0 = 0t1 = -4
      • t0 &lt; t1 ==> 0 &lt; -4 ->
      • t0 - t1 &lt; 0 ==> 0 - (-4) &lt; 0 ==> -4 &lt; 0(4 溢出到 -4)true

      a2)

      • 实际值:t0 = 1t1 = 5
      • 溢出:t0 = 1t1 = -3
      • t0 &lt; t1 ==> 1 &lt; -4 ->
      • t0 - t1 &lt; 0 ==> 1 - (-3) &lt; 0 ==> -4 &lt; 0 (4 溢出到 -4) true

      所以只有t0 - t1 &lt; 0 才能给出正确的结果。

      没有溢出的例子显然对这两个测试都是正确的。

    5. delta = 5(以及更多)

      a1) 溢出
      (最小值 tor t0 是 -1 所以让我们从它开始)

      • 实际值:t0 = -1t1 = 4
      • 溢出:t0 = -1t1 = -4
      • t0 &lt; t1 ==> -1 &lt; -4 ->
      • t0 - t1 &lt; 0 ==> -1 - (-4) &lt; 0 ==> 3 &lt; 0 false

      a2) 溢出

      • 实际值:t0 = 1t1 = 6
      • 溢出:t0 = 1t1 = -2
      • t0 &lt; t1 ==> 1 &lt; -2 ->
      • t0 - t1 &lt; 0 ==> 1 - (-2) &lt; 0 ==> 3 &lt; 0 两项测试均失败

      b1) 没有溢出

      • t0 = -4, t1 = 1
      • -4 &lt; 1 是的
      • -4 - 1 &lt; 0 ==> 3 &lt; 0(-5 溢出到 3)false

    +-------------+-----------------------------+----------------------------+
    |  tests if   | delta <= size of half cycle | delta > size of half cycle |
    | t0 is less  |-----------------------------|----------------------------|
    |  than t1    |  overflow  |  no overflow   | overflow  |  no overflow   |
    |-------------|------------|----------------|-----------|----------------|
    |   t0 < t1   |      -     |       +        |     -     |       +        |
    |-------------|------------|----------------|-----------|----------------|
    | t0 - t1 < 0 |      +     |       +        |     -     |       +        |
    |-------------|------------|----------------|-----------|----------------|
    | t0 - t1 > 0 |      -     |       -        |     +     |       -        |
    +-------------+------------+----------------+-----------+----------------+
    

    【讨论】:

      【解决方案3】:

      API的引用其实是:

      由于数值溢出,超过大约 292 年(2^63 纳秒)的连续调用的差异将无法准确计算经过的时间。

      如果 t0 和 t1 相隔 292 年,您将遇到数值溢出。否则,比较或减法都可以正常工作。

      【讨论】:

      • 不正确....它们可以相隔仅 1ns,但如果溢出发生在它们之间,则 t0 &lt; t1t1 - t0 &gt; 0 不同
      【解决方案4】:

      使用任何方法。
      在最近的 290 年内不会有什么不同。
      你的程序(甚至 Java 本身)不会活这么久。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-04-21
        • 2010-12-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-26
        • 2023-03-31
        相关资源
        最近更新 更多