根据==

时间:2015-07-28 21:23:01

标签: ruby

我在Ruby中发现了Time的一些行为,在编写测试时我并不理解。我错过了什么或者这是一个真正的问题吗?

我可以按如下方式重现irb中的情况 - 首先创建一个Time并添加30秒:

t = Time.new(2007,01,15,11,15,30.1)
# => 2007-01-15 11:15:30 +0000 
t1 = t + 30
# => 2007-01-15 11:16:00 +0000 

然后创建另一个应该等于t1的时间:

t2 = Time.new(2007,01,15,11,16,0.1)
# => 2007-01-15 11:16:00 +0000

现在我希望t1t2相等,但它们不符合==。从粗略的实验中我看来==似乎有效,除非添加秒将t1移动到新的一分钟:

t1 == t2
# => false 

但是,如果您对其发送to_f,则==会返回true

t1.to_f == t2.to_f
# => true

只是为了确认没有任何纳秒秒数:

t1.nsec
# => 100000000 
t2.nsec
# => 100000000

===在joanbm和Vitalii Elenhaupt的答案后添加(很抱歉再次打开)

joanbm和Vitalii Elenhaupt指出t1.to_rt2.to_r会产生不同的结果。

但是...根据正常范围内的Ruby-Doc Time存储为自纪元以来纳秒秒数的63位整数 - 这表明浮点问题不应该'进入它。

那么......为什么如果Time存储为整数,并且#to_f#nsec可以产生相同的结果到9位小数,则==不能使用此信息认为这两次是平等的? (也许Ruby在等式测试中使用#to_r。)。

并且...可以安全地假设t1.to_f == t2.to_f将始终对9位小数进行准确的等式测试,这是比较Time个对象的最佳方法吗?

3 个答案:

答案 0 :(得分:4)

This is the notorious problem with inaccurate representation of floating point numbers in computers, quite unrelated to Ruby or Time class implementation.

You give to both objects floating numbers as seconds argument, and its (inaccurate) fractional part constructor stores as a rational number for its internal representation:

  --Main Project
    |-- core
    |-- android
    |-- desktop

dtto for the 2nd time:

t = Time.new(2007,01,15,11,15,30.1)
t.subsec    # (14073748835533/140737488355328)  <- may vary on your system
t.subsec.to_f    # 0.10000000000000142  <- this is not equal to 0.1 !!

Just use exact numerical types like t2 = Time.new(2007,01,15,11,16,0.1) t2.subsec # (3602879701896397/36028797018963968) t2.subsec.to_f # 0.1 <- there accidentally precision didn't get lost and you are done:

Rational

Or do a rough comparison with t = Time.new(2007,01,15,11,15,Rational('30.1')) t1 = t + 30 t2=Time.new(2007,01,15,11,16,0.1r) # alternate notation, see docs t1 == t2 # true method applied on both sides.

答案 1 :(得分:0)

您正在为Time对象使用不同的参数

#irb
t = Time.new(2007,01,15,11,15,30,0.1)
=> 2007-01-15 11:15:30 +0000
t1 = t2 + 30
=> 2007-01-15 11:16:00 +0000

如果你没有通过会议记录,我的系统上的时区似乎发生了变化:

t2 = Time.new(2007,01,15,11,16,0.1)
=> 2007-01-15 11:16:00 +0100 # what happened here? '+0100'

t1 == t2
=> false

即使那个不是的情况,有趣的是这发生在我的irb中。 使用显式时区构建像Time.utc(2007,01,15,11,15,30,0.1)这样的对象,完全返回您描述的行为。

但是,如果传递相同数量的参数(t2为0分钟),则比较将返回预期结果,因为您可能甚至不会遇到joanbm描述的浮点问题。 / p>

t = Time.new(2007,01,15,11,15,30,0.1)
=> 2007-01-15 11:15:30 +0000 

t1 = t + 30
=> 2007-01-15 11:16:00 +0000 

t2 = Time.new(2007,01,15,11,16,0,0.1)
=> 2007-01-15 11:16:00 +0000 

t1 == t2
=> true

如果你打印时间理性表格,你会得到:

t1.to_r
=> (42112611033072059383231283/36028797018963968) 
t2.to_r
=> (42112611033072059383231283/36028797018963968) 

to_r返回自epoch(或Unix时间)以来的秒数时间,只要日期,时间和时区匹配,t1和t2就没有理由不相等

不幸的是,我不能给出一个很好的解释,为什么会这样。 这看起来很有趣,当我找到这种行为的原因时我会回来。

修改

这并不能解释时区的变化,因此无法解释问题,但是你错过了t的逗号,所以你实际上传递了30.1而不是30秒。

t = Time.new(2007,01,15,11,15,30.1)

可能应该是

t = Time.new(2007,01,15,11,15,30,0.1)

但是这不会导致您的比较失败。

答案 2 :(得分:0)

之所以发生这种情况,是因为两次都不同。这是this answer的一个很好的例证:

>> t1.to_r #=> (164501373566169071275213/140737488355328)
>> t2.to_r #=> (42112351632939282246454477/36028797018963968)

如果您不关心毫秒,可以使用to_i方法比较时间戳:

>> t1.to_i == t2.to_i # => true

或从时间戳创建一个新的Time对象:

>> Time.at(t1.to_i) == Time.at(t2.to_i) # => true