Почему я должен использовать 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) не будет жить так долго.