Please see the code and running result below:
#
# case one
#
[root@localhost tmp]# cat t5.rb
temp = 98.4
i = 0
begin
i += 1
puts "step" + i.to_s + " befre adding is " + temp.to_s
temp += 0.1
puts "step" + i.to_s + " after adding is " + temp.to_s
puts
end while temp < 98.6
[root@localhost tmp]# ruby t5.rb
step1 befre adding is 98.4
step1 after adding is 98.5
step2 befre adding is 98.5
step2 after adding is 98.6
OK I think the result is pretty right.
Now I change temp's original value to 98.3:
#
# case two
#
[root@localhost tmp]# cat t5.rb
temp = 98.3
i = 0
begin
i += 1
puts "step" + i.to_s + " befre adding is " + temp.to_s
temp += 0.1
puts "step" + i.to_s + " after adding is " + temp.to_s
puts
end while temp < 98.6
[root@localhost tmp]# ruby t5.rb
step1 befre adding is 98.3
step1 after adding is 98.4
step2 befre adding is 98.4
step2 after adding is 98.5
step3 befre adding is 98.5
step3 after adding is 98.6
step4 befre adding is 98.6
step4 after adding is 98.7
The output of step4 let me crazy.
Why the loop doesn't break after step3?
Because after step3 temp's value is 98.6, the loop condition was
checked, and the loop should be end.
Since I think case one is right, so case two get wrong result. Why?
Thank you.
Use BigDecimal if you wanna be really accurate.
Julian
Blog: http://random8.zenunit.com/
Twitter: http://twitter.com/random8r
Learn: http://sensei.zenunit.com/
New video up now at http://sensei.zenunit.com/ real fastcgi rails
deploy process! Check it out now!
>> 98.4 + 0.1 + 0.1 < 98.6
# => false
>> 98.3 + 0.1 + 0.1 + 0.1 < 98.6
# => true
--
Florian Frank
If you want accuracy, and don't care about performance, you could always use
rationals (fractions):
>> Rational(983,10) + Rational(1,10) + Rational(1,10) + Rational(1,10) <
Rational(986,10)
=> false
Of course, beware of trying to convert from floats to rationals:
>> Rational(98.3)
=> (6917247552664371/70368744177664)
If you look carefully:
>> Rational(98.3).denominator == 2**46
=> true
That also explains why floats behave strangely in general, for the newbies out
there. When we see 98.3, we see 98 and 3/10ths. But the computer doesn't use
10ths any more than it uses 10s, 100s, etc. It uses binary, powers of two. So
it converts those 3/10ths to some fraction of a power of two.
Unless, of course, you're on a mainframe. Then you can do packed decimal
arithmetic... But it'll be slower and perversely hard to work with for other
reasons.
So my compromise is, when I want to work with some fraction of a number, and I
want it to be as precise as possible (with no regard for performance), I make
it a Rational as long as I can get away with. I can always call to_f on the
end result.
I might be Doing It Wrong, though. Maybe BigDecimal is the way to go?
>
> So my compromise is, when I want to work with some fraction of a number,
> and I
> want it to be as precise as possible (with no regard for performance), I
> make
> it a Rational as long as I can get away with. I can always call to_f on the
> end result.
>
> I might be Doing It Wrong, though. Maybe BigDecimal is the way to go?
>
>
BigDecimal is probably easier to code with, but can kill performance as has
been mentioned previously. Recalling the old Fortran days, we always had a
constant called EPS we used in these kind of situations. EPS (short for
epsilon) was set to a small number, usually 1e-7. Anytime we needed to
compare two floating point values, we would not compare them directly, but
rather compare the absolute value of their difference to EPS.
In the original poster's code, here is how we would have dealt with the
issue:
eps = 1e-7
temp = 98.3
i = 0
begin
i += 1
puts "step" + i.to_s + " befre adding is " + temp.to_s
temp += 0.1
puts "step" + i.to_s + " after adding is " + temp.to_s
puts "temp - 98.6: #{temp-98.6}"
puts
#end while temp < 98.6
end while (temp - 98.6) < -eps
Having to remember to do this all the time is difficult and annoying.
-Doug Seifert
>> 98.3.step(98.6, 0.1).to_a
# => [98.3, 98.4, 98.5, 98.6]
>> 98.4.step(98.6, 0.1).to_a
# => [98.4, 98.5, 98.6]
>> (98.3..98.6).step(0.1).to_a
# => [98.3, 98.4, 98.5, 98.6]
>> (98.4..98.6).step(0.1).to_a
# => [98.4, 98.5, 98.6]
Maybe the general advice here is, don't do it yourself, if Ruby offers a
better way itself.
--
Florian Frank
I think it is. Rational makes sense if you're starting from an integer
division. But the OP is starting from a float (98.3), so BigDecimal is
probably the way to go.
Best,
--
Marnen Laibow-Koser
http://www.marnen.org
mar...@marnen.org
--
Posted via http://www.ruby-forum.com/.