ruby - 为什么从红宝石中的浮点数(以毫秒精度)转换为时间会导致红宝石中的精度损失不一致?
问题描述
当使用该Time.at
函数转换表示自纪元以来的秒数的浮点数(以毫秒精度)然后提取微秒值时,结果是精度损失不一致。
例如,使用自 1970 年以来每年的 1 月 1 日:
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
)。
解决方案
TL;DR(来自Time#to_f
)
请注意,IEEE 754 双精度不足以表示自纪元以来的确切纳秒数。
关于您的期望:
我本来预计亚秒值总是会损失精度(即始终是 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
推荐阅读
- html - 带下划线的文本,尽管 `text-decoration: none;` 和 ` 旁边的白线` 部分
- php - Laravel 背包。1-1(一对一)关系
- swift - 通知的定时器间隔和日历触发可能吗?
- python - 使用自定义损失函数运行 Light gbm 时 Python 崩溃
- asp.net-mvc - Automapper 仅将子对象重新映射到现有的扁平化 VM
- python - Matplotlib:来自 3 列熊猫数据框的 pcolormesh 或 pcolor
- ios - Xcode10 随机弹出“无法读取 XCode10Project.app 的包标识符。”
- c# - 为什么在浏览器中手动删除 Anti-forgery cookie 并发布会出现 404(.net core 2.0Razor 页面)
- amazon-web-services - 无法访问函数返回列表中的已处置对象?
- java - 如何自定义适配器以限制要显示的项目