From: Tanaka Akira Date: 2013-02-12T09:44:39+09:00 Subject: [ruby-core:52137] Re: [ruby-trunk - Bug #7829][Open] Rounding error in Ruby Time 2013/2/12 loirotte (Philippe Dosch) : > Bug #7829: Rounding error in Ruby Time > https://bugs.ruby-lang.org/issues/7829 > Even if I know the precision errors related to the implementation of IEEE 754 floating values, I'm very surprised of: > > irb(main):001:0> Time.utc(1970,1,1,0,0,12.860).strftime("%H:%M:%S,%L") > => "00:00:12,859" > > The fact is that I obtain: > > irb(main):002:0> Time.utc( 1970, 1, 1, 0, 0, 12.860 ).subsec > => (60517119992791/70368744177664) > irb(main):003:0> Time.utc( 1970, 1, 1, 0, 0, 12.860 ).subsec.to_f > => 0.8599999999999994 1. Ruby parser converts 12.860 to 12.8599999999999994315658113919198513031005859375. 12.860 is converted to IEEE 754 double value at Ruby parser. The IEEE 754 double value is actually 12.8599999999999994315658113919198513031005859375. % ruby -e 'puts "%.100g" % 12.860' 12.8599999999999994315658113919198513031005859375 Or 0x1.9b851eb851eb80000000p+3, in hexadecimal. % ruby -e 'puts "%.20a" % 12.860' 0x1.9b851eb851eb80000000p+3 So Time.utc takes the value and Time#subsec returns the value under the point. % ruby -e 'v = 12.860.to_r - 12; puts v, v.to_f' 60517119992791/70368744177664 0.8599999999999994 The Time object records the value given as is. A proposal to change (fix) this behavior: http://www.slideshare.net/mrkn/float-is-legacy 2. Time.strftime("%L") doesn't round, but floor. %L (and %N) in Time.strftime doesn't round the value but floor the value. Since 3-digits under the point of 0.8599999999999994315658113919198513031005859375 is "859", %L shows "859". rounding is not appropriate here. It is clearely unexpected that %L for 0.99999 shows "1000". 3. Use Time#round. There is a method to rounding Time: Time#round. If you needs a Time value rouinding 3-digits under the second, use time.round(3). % ruby -e 'p Time.utc(1970,1,1,0,0,12.860).round(3).strftime("%H:%M:%S,%L")' "00:00:12,860" -- Tanaka Akira