when 1.6 != 1.6? -- newbie

M

Mel Bohince

Really enjoying Ruby so far, but now I'm confused. To help learn Ruby,
I've been translating the Java code in the "Head First Design Patterns"
book into Ruby, aware that many are built-in with Ruby. While mimicking
the Decorator pattern I'm getting a strange failure to an assert_equal.

The unit test excerpts:
@e = Expresso.new
@e.setSize('grande')
@e = Mocha.new(@e)
assert_equal(1.40, @e.cost(),"price check") --> pass
@e = Mocha.new(@e)
assert_equal("Expresso, Mocha, Mocha", @e.getDescription()) --> pass
#puts @e.cost() --> 1.6 ##sanity check, should be 1.00 + 0.20 + 0.20 +
0.20 = 1.6
#####
assert_equal(1.6, @e.cost(),"price check2") --> fail with:

1) Failure:
testConcreteCondiment(TestBeverages)
[/Users/mel/Documents/Ruby_files/patterns/decorator/test/
testbeverages.rb:70]:
price check2.
<1.6> expected but was
<1.6>.
5 tests, 21 assertions, 1 failures, 0 errors

Recreated in irb, the instance looks like:
=> #<Mocha:0x7a884 @beverage=#<Mocha:0x21c94
@beverage=#<Expresso:0x58310 @description="Expresso", @size=0.2>>>

Any clues to what I'm doing wrong? Is there a strategy to debug this
kind of thing? The debugger is not like I'm use too.

Thanks for any help you can offer.

Ruby 1.8.2 on OSX 10.3.8
-: -: -: -: -: -: -: -: \|/
-: -: -: -: -: -: -: -: cU
Mel Bohince
Project Manager,
Arkay Packaging Corporation
(e-mail address removed)
 
F

Florian Frank

Mel said:
Any clues to what I'm doing wrong?


When you're using assert_equal, you're comparing two floating point
numbers with ==. That's usually a bad idea, because rounding errors
can/will occur. Try using assert_in_delta(number1, number2, delta), to
find out, if |number1 - number2| <= delta instead.

Another tip: If you want to represent prices in your programs, don't use
floats at all. Use integers for the representation of the cent values
and all computations, and divide by 100 only for the string
representation of the numbers.
 
I

Ilmari Heikkinen

<1.6> expected but was
<1.6>.

Floating point accuracy, the bane of mankind.
1.0 + 0.2 + 0.2 + 0.2 - 1.6
=> -2.22044604925031e-16
Any clues to what I'm doing wrong? Is there a strategy to debug this
kind of thing? The debugger is not like I'm use too.

Either do fixed-point decimals with integers and decimal point
divisor (100 => 1.00*100; 100 + 20 + 20 + 20 - 160 => 0)
or pick an error threshold and check that (a - b) < threshold
(1.0 + 0.2 + 0.2 + 0.2 - 1.6) < 0.01
=> true
 
J

Joe Van Dyk

Really enjoying Ruby so far, but now I'm confused. To help learn Ruby,
I've been translating the Java code in the "Head First Design Patterns"
book into Ruby, aware that many are built-in with Ruby. While mimicking
the Decorator pattern I'm getting a strange failure to an assert_equal.

I've found that a lot of those classical design patterns don't really
make much sense when using a duck-typed language.

Thoughts?
 
N

Neil Stevens

Either do fixed-point decimals with integers and decimal point
divisor (100 => 1.00*100; 100 + 20 + 20 + 20 - 160 => 0)
or pick an error threshold and check that (a - b) < threshold
(1.0 + 0.2 + 0.2 + 0.2 - 1.6) < 0.01
=> true

Just use BigDecimal. No need to get tricky.
 
M

Mark Hubbart

Really enjoying Ruby so far, but now I'm confused. To help learn Ruby,
I've been translating the Java code in the "Head First Design Patterns"
book into Ruby, aware that many are built-in with Ruby. While mimicking
the Decorator pattern I'm getting a strange failure to an assert_equal.

The unit test excerpts:
@e = Expresso.new
@e.setSize('grande')
@e = Mocha.new(@e)
assert_equal(1.40, @e.cost(),"price check") --> pass
@e = Mocha.new(@e)
assert_equal("Expresso, Mocha, Mocha", @e.getDescription()) --> pass
#puts @e.cost() --> 1.6 ##sanity check, should be 1.00 + 0.20 + 0.20 +
0.20 = 1.6
#####
assert_equal(1.6, @e.cost(),"price check2") --> fail with:

1) Failure:
testConcreteCondiment(TestBeverages)
[/Users/mel/Documents/Ruby_files/patterns/decorator/test/
testbeverages.rb:70]:
price check2.
<1.6> expected but was
<1.6>.
5 tests, 21 assertions, 1 failures, 0 errors

Recreated in irb, the instance looks like:
=> #<Mocha:0x7a884 @beverage=#<Mocha:0x21c94
@beverage=#<Expresso:0x58310 @description="Expresso", @size=0.2>>>

Any clues to what I'm doing wrong? Is there a strategy to debug this
kind of thing? The debugger is not like I'm use too.

Thanks for any help you can offer.

Here's a nice page that with a self-explanitory title:

"What Every Computer Scientist Should Know About Floating-Point Arithmetic"
<http://docs.sun.com/source/806-3568/ncg_goldberg.html>

The way money is usually handled is to either use integers for
pennies, or use a separate money class which stores money in that way.
A nice thing about doing this in Ruby is that once you create your
money class, you can add helper methods to Numeric:

class Numeric
def cents
Money.new(self.round)
end
def dollars
Money.new((self*100).round)
end
end

23.cents #==> $0.23
23.50.dollars #==> $23.50

cheers,
Mark
 
A

Aleksi

Ilmari said:
Floating point accuracy, the bane of mankind.
1.0 + 0.2 + 0.2 + 0.2 - 1.6
=> -2.22044604925031e-16



Either do fixed-point decimals with integers and decimal point
divisor (100 => 1.00*100; 100 + 20 + 20 + 20 - 160 => 0)
or pick an error threshold and check that (a - b) < threshold
(1.0 + 0.2 + 0.2 + 0.2 - 1.6) < 0.01
=> true

Ilmari is right here. The way to check if the calculated value is close
enough to the expected value, please check:

http://www.ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit/Assertions.html#M000104

Usage being something like

required_accuracy = 0.0001
assert_equal(1.6, @e.cost(), required_accuracy,
"price check2") # --> does not fail anymore

- Aleksi
 
M

Mel Bohince

Try using assert_in_delta(number1, number2, delta), to
find out, if |number1 - number2| <= delta instead.

Thanks to all for the help!

I'll remember the assert_in_delta in the future when testing on floats.

The point about avoiding float when doing currency is sound advice, yet
opens a new chapter of issues when deal with big numbers in special
pricing units of measure. Then Googled "Class Money" got a spiffy
"ruby" Money at <http://www2a.biglobe.ne.jp/~seki/ruby/sbpp8.html> and
a nice interface from junit.samples.money; ah, other have blazed and
paved the trail.

Thanks.
-: -: -: -: -: -: -: -: \|/
-: -: -: -: -: -: -: -: cU
mel
 
M

Mel Bohince

I've found that a lot of those classical design patterns don't really
make much sense when using a duck-typed language.
Thoughts?
Joe,

I'll defer to experienced, but yes, it seems like overkill when not a
strongly-typed lng.

I just wanted some hands-on experience as I read the Pick-Ax; thought
that working with some classic coding situations would be a good
contrived problem that would help me retain Ruby. (I spend most of my
work day writing in 4D which is not OO.) Also, the "Head First" book is
really motivating; the writing style is truly impressive.

-: -: -: -: -: -: -: -: \|/
-: -: -: -: -: -: -: -: cU
mel
 

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

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top