Почему я должен использовать t1 - t0 <0, а не t1 <t0, при использовании System.nanoTime() в Java

Когда я читал System.nanoTime() API в Java. Я нашел эту строку:

следует использовать t1 - t0 <0, а не 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 <0, а не t1 <t0 из-за возможности численного переполнения.

Я хочу знать, почему t1 - t0 < 0 является предпочтительным способом предотвращения переполнения.

Потому что я читал из другой ветки, что A < B предпочтительнее, чем A - B < 0.

Java Integer compareTo() - зачем использовать сравнение против вычитания?

Эти две вещи противоречат друг другу.

Ответы

Ответ 1

Время Nano не является "реальным" временем, это просто счетчик, который увеличивается, начиная с некоторого неуказанного числа, когда происходит какое-то неуказанное событие (возможно, компьютер загружен).

Он переполнится и в какой-то момент станет отрицательным. Если ваш t0 находится перед переполнением (т.е. очень большим положительным), а ваш t1 сразу после (очень большое отрицательное число), то t1 < t0 (т.е. Ваши условия неверны, потому что t1 произошло после t0).....

Но, если вы скажете t1 - t0 < 0, ну, магия в том, что a для тех же причин переполнения (undeflow) (очень большой отрицательный вычитает очень большой положительный забой), результатом будет количество наносекунд, что t1 после t0..... и будет правильным.

В этом случае две ошибки действительно делают правильно!

Ответ 2

t0 - t1 < 0 лучше, чем t0 < t1, когда мы уверены, что реальная разница значений (до переполнения) не больше половины или размера набора, содержащего все возможные значения.
Для наносекунд это будет приблизительно 292 года (наносекунды хранятся в длинном и половинном размерах long 2^64/2= 2^63 наносекундах ~ = 292 года).

Итак, для временных выборок, разделенных менее чем 292 годами, мы должны использовать t0 - t1 < 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 < 0 и t0 < t1 будут вести себя для значений, где разница будет и не будет больше 4 (половина размера цикла и -4 минимальное значение, что означает, что это может быть минимальный результат для вычисления дельта). Обратите внимание, что только t0 - t1 < 0 даст правильные результаты при переполнении t1

  • delta = 1 с переполнением большего значения (notice): мы не делаем меньшего переполнения значений, потому что это будет означать что оба значения находятся в одном цикле, поэтому вычисления будут такими же, как если бы не было переполнения)

    • действительные значения: t0 = 3 t1 = 4
    • overflowed: t0 = 3 t1 = -4
    • t0 < t1 == > 3 < -4 false
    • t0 - t1 < 0 == > 3 - (-4) < 0 == > -1 < 0 (7 переполняется до -1) true

    поэтому только для t0 - t1 < 0 мы получили правильный результат, несмотря на или, возможно, благодаря переполнению.

  • delta = 1, но на этот раз без переполнения

    a) положительные значения

    • t0 = 2, t1 = 3
    • 2 < 3 true
    • 2 - 3 < 0 == > -1 < 0 true

    b) отрицательные значения

    • t0 = -4, t1 = -3
    • -4 < -3 true
    • -4 - (-3) < 0 == > -1 < 0 true

    для остальных случаев, где вещественная дельта = 1, мы также получим правильные результаты для тестов t0 < t1 и t0 - t1 < 0 (t0 - t1 будет всегда -1)

  • delta = 3 (почти половина цикла)

    a1) с переполнением большего значения

    • реальные значения: t0 = 3 t1 = 6
    • overflowed: t0 = 3 t1 = -2
    • t0 < t1 == > 3 < -2 false
    • t0 - t1 < 0 == > 3 - (-2) < 0 == > -3 < 0 (5 переполнений до -3) true

    a2) другой случай с переполнением

    • действительные значения: t0 = 2 t1 = 5
    • переполнен: t0 = 2 t1 = -3
    • t0 < t1 == > 2 < -3 false
    • t0 - t1 < 0 == > 2 - (-3) < 0 == > -3 < 0 (снова 5 переполнений до -3) true


    Таким образом, снова только t0 - t1 < 0 дал правильный результат.

    b) без переполнения t0 - t1 всегда будет равен -3 (-delta), поэтому это всегда будет давать правильный результат. t0 < t1 также даст правильный откат

    • реальные значения: t0 = -1 t1 = 2
    • t0 < t1 == > -1 < 2 true
    • t0 - t1 < 0 == > -1 - 2 < 0 == > -3 < 0 true
  • delta = 4 результат t0 - t1 всегда будет равен -4, поэтому он также будет <0.

    примеры с переполнением
    а1)

    • действительные значения: t0 = 0 t1 = 4
    • переполнен: t0 = 0 t1 = -4
    • t0 < t1 == > 0 < -4 false
    • t0 - t1 < 0 == > 0 - (-4) < 0 == > -4 < 0 (4 переполнения до -4) true

    a2)

    • действительные значения: t0 = 1 t1 = 5
    • переполнен: t0 = 1 t1 = -3
    • t0 < t1 == > 1 < -4 false
    • t0 - t1 < 0 == > 1 - (-3) < 0 == > -4 < 0 (4 переполнения до -4) true

    Итак, снова только t0 - t1 < 0 дают правильные результаты.

    Примеры без переполнения, очевидно, будут верны для обоих тестов.

  • delta = 5 (и более)

    a1) с переполнением (минимальное значение tor t0 равно -1, поэтому давайте начнем с него)

    • действительные значения: t0 = -1 t1 = 4
    • переполнен: t0 = -1 t1 = -4
    • t0 < t1 == > -1 < -4 false
    • t0 - t1 < 0 == > -1 - (-4) < 0 == > 3 < 0 false

    a2) с переполнением

    • действительные значения: t0 = 1 t1 = 6
    • переполнен: t0 = 1 t1 = -2
    • t0 < t1 == > 1 < -2 false
    • t0 - t1 < 0 == > 1 - (-2) < 0 == > 3 < 0 false оба теста не удалось

    b1) без переполнения

    • t0 = -4, t1 = 1
    • -4 < 1 true
    • -4 - 1 < 0 == > 3 < 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 года, вы столкнетесь с численным переполнением. В противном случае сравнение или вычитание будут работать нормально.

Ответ 4

Использовать любой метод.
Это будет не разница в ближайшие 290 лет.
Ваша программа (и даже сама Java) не будет жить так долго.