really crazy about this (do/while)

R

Ruby Newbee

Hello,

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.
 
F

Florian Frank

Ruby said:
Since I think case one is right, so case two get wrong result. Why?
Thank you.
Welcome to the wonderful world of floating point numbers:
# => true
 
D

David Masover

Welcome to the wonderful world of floating point numbers:

# => false


# => true

If you want accuracy, and don't care about performance, you could always use
rationals (fractions):
Rational(986,10)
=> false

Of course, beware of trying to convert from floats to rationals:
=> (6917247552664371/70368744177664)

If you look carefully:
=> 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?
 
D

Douglas Seifert

[Note: parts of this message were removed to make it a legal post.]
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
 
F

Florian Frank

David said:
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?
Another possibility is using a small, but >0, epsilon and check if the
distance between the number and the limit is less than its value. Ruby
does this automatically for you, if you use its #step functions:
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.
 
M

Marnen Laibow-Koser

David Masover wrote:
[...]
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?

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,
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,164
Messages
2,570,898
Members
47,439
Latest member
shasuze

Latest Threads

Top