【问题标题】:How to compare two nano time values? [javadoc confusion]如何比较两个纳米时间值? [javadoc 混乱]
【发布时间】:2016-03-03 23:17:43
【问题描述】:

我已经阅读了System.nanoTime() 的 javadoc,一切似乎都很清楚。直到我到达最后一段:

比较两个 nanoTime 值

long t0 = System.nanoTime();

...

long t1 = System.nanoTime();

应该使用 t1 - t0

有两点我不清楚:

  1. 如果t1t0 之后被占用,为什么还要检查t1 < t0?我的理解是纳米时间总是在增加。所以,我宁愿检查t1 > t0
  2. 假设这是一个错字,他们的意思是正确的检查是t1 - t0 > 0。我仍然不明白为什么这是正确的检查方式而不是t1 > t0。他们提到了数字溢出,我不太明白他们的意思。关于数值溢出,这里有提到:

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

好的,所以由于纳米时间是作为长值存储的,所以最终会在 292 年之后溢出。接下来会发生什么?它是否从头开始,即最低的负值-2 ^ 63?还是它总是停止测量并返回 (2^63 - 1)?

【问题讨论】:

  • 数值溢出总是一样的。例如Integer.MAX_VALUE + 1 = Integer.MIN_VALUE.
  • 这也只是一种解决方法...... 584 年后,即使 t1-t0<0 也不再适用;)
  • @ferrybig,这不是真的。正如 Javadoc 所指出的,纳米时间使用与系统时间无关的固定原始时间,并且在 JVM 终止之前不会改变。随系统时钟变化的是 System.currentTimeMillis() 。仅当此处的大多数答案中已解释的数字溢出时,纳米时间才会返回“较早”的时间。

标签: java time comparison integer-overflow


【解决方案1】:

您是对的,您引用的文档部分似乎有些混乱。

但是,重要的文档部分是:

此方法只能用于测量经过的时间,与系统或挂钟时间的任何其他概念无关。返回的值表示自某个固定但任意的原始时间(可能在将来,因此值可能为负)以来的纳秒。在 Java 虚拟机实例中,此方法的所有调用都使用相同的来源;其他虚拟机实例可能使用不同的来源。

(重点由我添加。)

这意味着您无法保证您的代码不会在产生的 long 值碰巧从正值变为负值时运行。

没有什么可以保证这会在 300 年后发生,它可能会在今天发生。

目前,我的 JVM 返回一些数字,例如 3496793269188,但如果它愿意,它可能会返回一些非常接近 9223372036854775807 的数字(即Long.MAX_VALUE),这将使得从正面到负面的翻转迫在眉睫。

所以,你应该采取一切必要的预防措施。

【讨论】:

  • Long.compare(t0, t1) 对溢出没有任何作用。
  • @user2357112 O_O 你是对的,我认为它有,但事实证明它没有,现在想起来,它不能。呸!我不知道为什么我不得不提到它。我现在就删除它。
  • 这个答案以及其他解释溢出算法的答案帮助了我,现在一切都清楚了。谢谢:)
【解决方案2】:

好吧,javadoc 说的是实话。考虑这样的例子:

        long t0 = Long.MAX_VALUE;
        long t1 = Long.MIN_VALUE;

        System.out.println(t1 < t0);
        System.out.println(t1 - t0 < 0);

它会给你

true
false

虽然在数学上这两个表达式都是正确的。但在我们的例子中,我们知道,time 的负值意味着它被溢出,因此它应该真的大于然后是正数。

【讨论】:

    【解决方案3】:

    当 t1 在 t0 之后(t1

    如果读取的无符号数大于有符号的正数,则更基本的负数:https://en.wikipedia.org/wiki/Signed_number_representations

    【讨论】:

      【解决方案4】:

      让我从你问题的结尾开始。是的,如果值大于 2^63-1,值将溢出。溢出的常见实现是存储结果的最低可表示位; value 将 wrap(您可以在我的帖子底部看到关于溢出的信息以供参考)

      序列如:2^63-2, 2^63-1, -2^63, -(2^63-1) ...

      现在回顾 Javadoc,我同意关于使用比较的解释令人困惑,我们自然会尝试比较 t1 &gt; t0 以验证 t1 是否发生在 t0 之后。 你说对了一部分。虽然我不认为这是一个错字,但没有正确解释。我觉得应该说:

      对于t0t1 两个值(其中t1 在t0 之后捕获),您不应使用t1 &lt; t0(检查false)而应使用t1 - t0 &lt; 0,同样您不应使用@987654331 @(检查true)而是t1 - t0 &gt; 0

      或者将其形式化:

      对于两个 'nano' 值 t0t1,其中 t1t0 之后捕获,以下表示:(t1 - t0 &gt; 0) == true 只要t1t0

      为什么?因为即使t1 溢出(所以它是负数),结果t1 - t0 也会是负数但是,它会小于-2^64 并且它会“溢出”到正值!强>。

      只要满足t0t1 之间的距离条件,这就是成立的!如果距离大于 2^64 例如:对于t0 = 1; t1 = -(2^64-2),减法结果将是:t1 - t0 = -(2^64-1),因此指定条件(t1 - t0 &gt; 0)将给出不正确的结果。

      对吗?:)

      溢出时

      为了便于解释,lats 假设使用 8 位存储的类型(而不是 long 使用的 64 位),因此二进制到十进制表示为:

      0000 0000 => 0
      0000 0001 => 1 (2^0)
      0000 0010 => 2 (2^1 + 2^0)
      ...
      1111 1111 => 255 (2^7 + 2^6 + ... + 2^1)
      

      现在下一个数字自然是你要加 1 的时候。 将 1 加到二进制 1111 1111 将产生 1 0000 0000

      (1) 0000 0000 =&gt; -256 !!! 通常位置 8 上的溢出位将代表一个(负权重)符号位

      first following value is adding 1 to most-right position 
      (1) 0000 0001 => -256 + 2^0 = -255
      (1) 0000 0010 => -256 + 2^1 = -254
      (1) 0000 0011 => -256 + 2^1 + 2^1 = -253
      ...
      

      你得到了图片

      这源于依赖二进制值的底层硬件注册表实现。 例如,您可以在此处阅读更详细的说明:http://www.allaboutcircuits.com/textbook/digital/chpt-2/binary-overflow/

      【讨论】:

        猜你喜欢
        • 2016-08-06
        • 2013-11-13
        • 1970-01-01
        • 2021-04-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-04
        • 1970-01-01
        相关资源
        最近更新 更多