Closed chittoor closed 12 years ago
This is expected behavior. You should be able to get more accuracy by adding an integer value and then adding the floating point value:
dt + 35373468 / 86400 + (35373468 % 86400)/86400.0
Was expecting you'd say that :) Closing this. Btw there is a big time gotcha when mathn is involved though. Catches almost everyone by surprise.
>> require 'mathn'
=> true
>> (35373468 / 86400).class
=> Rational
>> dt + 35373468 / 86400 + (35373468 % 86400)/86400.0
=> Sun, 14 Apr 2013 15:55:41 -0500
>>
?>
?> dt + (35373468 / 86400).to_i + (35373468 % 86400)/86400.0
=> Sun, 14 Apr 2013 05:57:53 -0500
Just for clarification, home_run operates similarly to the stdlib (both the <= 1.9.2 pure ruby code and the >=1.9.3 switch_hitter C extension):
$ ruby18 -v -r date -S irb
ruby 1.8.7 (2012-02-08 patchlevel 358) [x86_64-openbsd]
irb(main):001:0> dt = DateTime.parse("Wed Feb 29 20:00:05 -0500 2012")
=> #<DateTime 2012-02-29T20:00:05-05:00>
irb(main):002:0> ndt = dt + 35373468 / 86400.0
=> #<DateTime 2013-04-14T05:57:52-05:00>
irb(main):003:0> ndt.sec
=> 52
irb(main):004:0> ndt.sec_fraction
=> 1.15740740509259e-05
irb(main):005:0> ndt.sec_fraction * 86400
=> 0.999999998
$ ruby19 -v -r date -S irb
ruby 1.9.3p125 (2012-02-16 revision 34643) [x86_64-openbsd]
irb(main):001:0> dt = DateTime.parse("Wed Feb 29 20:00:05 -0500 2012")
=> #<DateTime: 2012-02-29T20:00:05-05:00 ((2455988j,3605s,0n),-18000s,2299161j)>
irb(main):002:0> ndt = dt + 35373468 / 86400.0
=> #<DateTime: 2013-04-14T05:57:52-05:00 ((2456397j,39472s,999999998n),-18000s,2299161j)>
irb(main):003:0> ndt.sec
=> 52
irb(main):004:0> ndt.sec_fraction
=> (499999999/500000000)
irb(main):005:0> ndt.sec_fraction * 86400
=> (13499999973/156250)
Of course, in the stdlib, you can use rationals for precision, which doesn't work well in home_run, as it will convert the rationals to floats.
Actually, my mistake, in the <= 1.9.2 stdlib, you get:
$ ruby18 -I /usr/local/lib/ruby/1.8 -v -r date -S irb
ruby 1.8.7 (2012-02-08 patchlevel 358) [x86_64-openbsd]
irb(main):001:0> dt = DateTime.parse("Wed Feb 29 20:00:05 -0500 2012")
=> #<DateTime: 42439464721/17280,-5/24,2299161>
irb(main):002:0> ndt = dt + 35373468 / 86400.0
=> #<DateTime: 2456396.95686343,-5/24,2299161>
irb(main):003:0> ndt.sec
=> 53
irb(main):004:0> ndt.sec_fraction
=> 5.65692353918424e-11
irb(main):005:0> ndt.sec_fraction * 86400
=> 4.88758193785519e-06
irb(main):007:0> ndt.strftime('%N')
=> "000004887"
You can see it is over by 4887 nanoseconds, instead of being under by 2 nanoseconds. So both >= 1.9.3 stdlib and home_run have much better accuracy with floats than the <= 1.9.2 stdlib.
Example: tested on 1.8.7 p358
This is not a remote/edge case issue. It's quite easy to catch. We have internally added a hack to the functions rhrdt__nanos_to_hms and rhrdt_sec_fraction to round the seconds up if it's close enough (100 ns). Ugly but seems to work without accidentally rounding off on DateTime.now and others which generate fractional seconds.
Please note that the floating point arith involved is still pretty accurate at this range.