Почему преобразование из секунд (с точностью до миллисекунды) в число с плавающей точкой во время в ruby приводит к непоследовательной потере точности в ruby? - PullRequest
0 голосов
/ 15 апреля 2020

При использовании функции Time.at для преобразования числа с плавающей запятой, представляющего секунды с начала эпохи (с точностью до миллисекунды), и последующего извлечения значения микросекунды, получается непоследовательная потеря точности.

Например, с использованием 1 января за каждый год, начиная с 1970 года:

ruby -e 'require("time");p (1..50).map {|offset| Time.at(Time.parse("#{1970+offset}-01-01T00:00:0.123}").to_r.numerator/1000.0).usec}'
[122999, 123000, 122999, 122999, 122999, 122999, 122999, 122999, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 123000, 122999, 122999, 122999, 122999, 122999, 122999, 122999, 122999, 122999, 122999, 122999, 122999, 122999, 122999, 122999, 122999]

Основываясь на обсуждении в https://bugs.ruby-lang.org/issues/7829 - я бы ожидал, что значение в секунду будет всегда иметь потерю точности (т.е. всегда будет 122999).

1 Ответ

1 голос
/ 15 апреля 2020

TL; DR (от Time#to_f)

Обратите внимание, что IEEE 754 double не является достаточно точным для представления точного количества наносекунд с начала эпохи.


Относительно вашего ожидания:

Я бы ожидал, что значение в секунду будет всегда иметь потерю точности (т. Е. Всегда будет 122999).

потеря ≠ меньше. * "потеря точности" не означает, что результат всегда на меньше , чем фактическое значение. Например, добавление 0.1 и 0.2 приводит к значению, немного превышающему 0.3 из-за потери точности:

0.1 + 0.2 #=> 0.30000000000000004

Назад к вашим значениям времени. Давайте возьмем первые два года, 1971 и 1972 годы:

Time.parse('1971-01-01T00:00:0.123').to_f #=> 31532400.123
Time.parse('1972-01-01T00:00:0.123').to_f #=> 63068400.123

Time.at(31532400.123).usec #=> 122999
Time.at(63068400.123).usec #=> 123000

Это происходит из-за неточностей с плавающей запятой. фактические значения с плавающей точкой:

31532400.1229999996721744537353515625
63068400.123000003397464752197265625
#        ^^^^^^
#         usec

Вызов nsec показывает, что оба вышеперечисленных факта в действительности неточны: (один немного ниже, другой немного выше)

Time.at(31532400.123).nsec #=> 122999999
Time.at(63068400.123).nsec #=> 123000003

Чтобы получить точные значения, вы должны предоставить точные аргументы:

Time.at(31532400123.quo(1000)).usec #=> 12300

# or

Time.at(31532400, 123000).usec #=> 12300
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...