0.06 == 0.06 returns false in Ruby?

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Dan Z. wrote:

Dan

Yeah … even people like me who have spent several decades in this
branch of computing need to be reminded of these things occasionally.
What I wish Ruby had for scientific number-crunching is built-in
floating point arrays, rather than having to pass potential large
objects into and out of C language libraries to get number crunching
done.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFG2DTf8fKMegVjSM8RAudJAJ9wOyvmdtgSRgVVUpbXem6nLUpfKQCdEspD
YgYD17sas15mJA/Q1kGMwJQ=
=kZx0
-----END PGP SIGNATURE-----

On Sat, 01 Sep 2007 00:24:39 +0900, M. Edward (Ed) Borasky wrote:

BigDecimal.

You’d think some fixed point math libraries would help, but be careful,
because many of those also store in twos compliment.

–Kyle

IIRC BigDecimal is in fact stored in (pregnant pause) Binary Coded
Decimal. But I should check that.

Neither one can represent 1/3 or sqrt(2) exactly.

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Kyle S. wrote:

You’d think some fixed point math libraries would help, but be
careful, because many of those also store in twos compliment.

–Kyle

IIRC BigDecimal is in fact stored in (pregnant pause) Binary Coded
Decimal. But I should check that.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFG2DKp8fKMegVjSM8RAsTQAKCNicl4roW3uh7LWLERNMxbtL0dnQCdHTAY
/Y2wWjyVegtTECn6Ur32v30=
=hAqx
-----END PGP SIGNATURE-----

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Ken B. wrote:

BigDecimal.
Neither one can represent 1/3 or sqrt(2) exactly.

Which is why there are libraries and packages like GiNaC, CLN, GMP,
Singular, Maxima, Axiom, …

I personally think Ruby’s libraries for exact arithmetic are better than
those in the other scripting languages – I don’t think anyone else has
BigDecimal, Rational, Complex, Matrix, “mathn” and Bignums built in or
part of the standard libraries. The only thing that’s missing, as I
noted earlier, is the ability to declare a physically contiguous block
of RAM as a vector of floating point or complex numbers and operate on
them as such. For that, you need to go to an external package like
NArray.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD4DBQFG2Ept8fKMegVjSM8RAiqFAJi/iWlXdhu/4UNm8y3WV+g6tlaCAKC5PDDS
yWzL3Cnk6zkYnMcA/Rl4dw==
=bW0a
-----END PGP SIGNATURE-----

On 31.08.2007 17:30, M. Edward (Ed) Borasky wrote:

questions on the list, i.e., "how come it sometimes works and

That’s probably the exact reason why not your wife or kids write
shot, I think.
Of course I can speak only for Germany but I had a mandatory lecture on
numerical analysis during my CS studies. IIRC it was even during the
lower grade phase (dunno the proper English wording), i.e. within the
first two years. But then again, the “Diplom” is probably considered
roughly equivalent to a major. Somehow I thought numerical effects were
so basic and common that they are mentioned in all other places as well.
Thanks for pointing this out.

Kind regards

robert

On Aug 31, 1:25 am, Michael U. [email protected] wrote:

2 Always use
(x-y).abs < (x.abs + y.abs) * Float::EPSILON)
as a test for equality.

Better than the first proposal, but won’t work if the rounding error
gets too large after a complex computation.
In addition, (1) and (2) suffer from the problem that x==y and y==z do
not imply x==z.

How about
(a - b).abs / [a, b].map{|x| a.abs}.min < Float::EPSILON
?

def eq( a, b )
(a - b).abs / [a, b].map{|x| a.abs}.min < Float::EPSILON
end

DATA.each{|s|
strings = s.chomp.split(“;”)
floats = strings.map{|s| eval(s) }
puts strings.join( " == " ) +
" : #{floats[0]==floats[1]} #{ eq( *floats ) }"
}

END
(0.05+0.01);0.06
(0.34+0.01);0.35
9.123456789;9.1234567891
9.123456789012345;9.1234567890123451
9.12345678901234e-9;9.123456789012341e-9

=== output ===
(0.05+0.01) == 0.06 : false true
(0.34+0.01) == 0.35 : false true
9.123456789 == 9.1234567891 : false false
9.123456789012345 == 9.1234567890123451 : false true
9.12345678901234e-9 == 9.123456789012341e-9 : false true

I am in the Sophomore year of a CS degree at Washing State University
and I am currently enrolled in a mandatory Numerical Computing class.
While it is very much more CS-than-math it does, of course, cover
topics like FP implementations and related issues.

On 8/31/07, Kyle S. [email protected] wrote:

Um, 2’s compliment is not a cause of floating point error. IEEE
floating point numbers do not use two’s complement, they have a sign
bit. Two’s complement is just a way of encoding the sign of a value.

You’d think some fixed point math libraries would help, but be
careful, because many of those also store in twos compliment.

Again, the use of two’s complement or lack thereof is totally
irrelevant.

From: Robert K. [mailto:[email protected]]

Well, you could provide your formula as strings and convert it to

something that creates BigDecimals along the way, like

irb(main):015:0> “0.01+0.05”.gsub(%r{\d+(?:.\d*)?},

“BigDecimal.new(‘\&’)”)

=> “BigDecimal.new(‘0.01’)+BigDecimal.new(‘0.05’)”

irb(main):016:0> eval(“0.01+0.05”.gsub(%r{\d+(?:.\d*)?},

“BigDecimal.new(‘\&’)”))

=> #BigDecimal:7ff6dd60,‘0.6E-1’,4(12)

note this is of course not a proper solution since the RX does not

match all valid floats

:slight_smile:
on my case, i just want ruby to default to bigdeci (or whatever i want)
instead of float. meaning, i choose any math processor i want. is that
possible now, or in near future of ruby?

kind regards -botp

On 01.09.2007 22:33, Kyle S. wrote:

BigDecimal.

Um, 2’s compliment is not a cause of floating point error. IEEE
floating point numbers do not use two’s complement, they have a sign
bit. Two’s complement is just a way of encoding the sign of a value.

Used the wrong term :stuck_out_tongue: sue me. I guess that’s why I should lookup
stuff before responding :wink:
Anyway it’s all based on the fact that you can’t store some numbers
exactly as sum of different xes for 2**(-x).

BigDecimals do not use binary representation. That’s why they are
called Big/Decimal/.

If you want to give a warning then it should be that for any
representation (binary, decimal, hex, octal whatever) there are
fractional numbers that cannot be represented properly with that
representation. You have to be aware of this for all representation
types. The reason people are surprised is typically because we enter
float numbers in decimal but systems use binary representation (which is
standardized btw.) internally and that has other numbers that it cannot
properly represent. BigDecimal helps because it uses the same
representation internally that we are used to use for numbers - decimal
digits.

Kind regards

robert

On 8/31/07, Logan C. [email protected] wrote:

BigDecimal.

Um, 2’s compliment is not a cause of floating point error. IEEE
floating point numbers do not use two’s complement, they have a sign
bit. Two’s complement is just a way of encoding the sign of a value.

Used the wrong term :stuck_out_tongue: sue me. I guess that’s why I should lookup
stuff before responding :wink:
Anyway it’s all based on the fact that you can’t store some numbers
exactly as sum of different xes for 2**(-x).

–Kyle

On 31.08.2007 16:07, Davor wrote:

Hi,
Try converting to strings e.g:
irb(main):004:0> (0.05 + 0.01).to_s == 0.06.to_s
=> true

I think what puzzles is while 0.05+0.01 == 0.06 returns false, the
strings do match.

def float_check(a,b)
p [a==b,a.to_s == b.to_s]
end

float_check(0.06,0.06) # -> [true,true]
float_check(0.05+0.01,0.06) # -> [false,true]

Most other programming languages either have no format-less .to_s
function (like in C) or return values at inspection time so that
people get aware of the issue more easily. In Python it is obvious
that 0.05+0.01 == 0.06 is false.

$ python

0.05
0.050000000000000003

0.01
0.01

0.05+0.01
0.060000000000000005

0.06
0.059999999999999998

I don’t understand why Ruby ‘rounds’ values with .inspect (and
.to_s) opposed to Marshal.dump. Compare its output with the one
Python gives (this code is for Ruby 1.8.6):

[0.05,0.01,0.05+0.01,0.06].each do |v|
p Marshal.dump(v).sub(/…([^\000])./,’\1’)
end

Output:

“0.050000000000000003”
“0.01”
“0.060000000000000005”
“0.059999999999999998”

Did I miss something here, is there a way to make Float#to_s (and,
thus, Float#inspect) give the same output as Marshal?

  • Matthias

Matthias Wächter wrote:

I don’t understand why Ruby ‘rounds’ values with .inspect (and
.to_s) opposed to Marshal.dump. Compare its output with the one
Python gives (this code is for Ruby 1.8.6):

Because for an inspection a precision of 5, 6 places are sufficient.
Inspect isn’t used for printing values other than debugging etc.
In the opposite, having 40+ places for every float in your debug output
would be rather annoying and almost always of little help.
If python choses to be annoying, be it so, I’m happy ruby doesn’t.
Use sprintf/String#% if you want to specify the precision you want your
output.
Marshal.dump on the other hand is serializing, of course you don’t want
your floats truncated just because you’re serializing.
As for having a way: you can always open the Float class and define
.to_s to your liking. Same for inspect.

Regards
Stefan

M. Edward (Ed) Borasky wrote:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Stefan R. wrote:

Marshal.dump on the other hand is serializing, of course you don’t want
your floats truncated just because you’re serializing.
Excuse me – does Marshal.dump serialize floats in text or in binary?
I’d hope it was binary. A YAML serializer would have to do it in text,
although they could obviously encode binary in base 64 or hex.

The number itself is in acsii:

irb(main):006:0> x = 1.234e120
=> 1.234e+120
irb(main):007:0> Marshal.dump(x)
=> “\004\bf\0371.2340000000000001e+120\000@`”

Maybe this is for compatibility, in case ruby is running on some oddball
system without the standard float formats?

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Stefan R. wrote:

Marshal.dump on the other hand is serializing, of course you don’t want
your floats truncated just because you’re serializing.
Excuse me – does Marshal.dump serialize floats in text or in binary?
I’d hope it was binary. A YAML serializer would have to do it in text,
although they could obviously encode binary in base 64 or hex.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFG3FVV8fKMegVjSM8RAlq1AKCCJbOHo7WXyZQUSwt2IWHb0rRiCwCfbYpU
JabnGpU8CXJgl/J1oeEgruE=
=GUe1
-----END PGP SIGNATURE-----

On Aug 31, 12:25 am, Michael U. [email protected] wrote:

Unfortunately, there is no easy solution to this problem.

Here’s my naive, DWIM approach:

class Float
def ==(other)
self.to_s == other.to_s
end
end

Regards,

Dan

Daniel B. wrote:

class Float
def ==(other)
self.to_s == other.to_s
end
end

Oh yes, nice one. This fulfills

‘from x==y and y==z follows x==z’

Unfortunately it does not fulfill some other properties one
wants to have; e.g. it violates

‘from x==y follows x+z==y+z’

for example

x, y = 1.0/3.0, 0.333333333333333
x.to_s == y.to_s # => true
(x+x).to_s == (y+x).to_s # => false

In particular, you don’t have

‘from x==y follows x-y==0’

for example

x, y = 0.6, 6 * 0.1
x.to_s == y.to_s # => true
(x - y).to_s == 0.to_s # => false

HTH,

Michael

From: Michael U. [mailto:[email protected]]

Daniel B. wrote:

> class Float

> def ==(other)

> self.to_s == other.to_s

> end

> end

Oh yes, nice one. This fulfills

‘from x==y and y==z follows x==z’

Unfortunately it does not fulfill some other properties one

w a little help fr bigdecimal, a little goes a long long way

class Float
def to_dec
BigDecimal.new(self.to_s)
end
end
=> nil
class Fixnum
def to_dec
BigDecimal.new(self.to_s)
end
end
=> nil

x=0.06.to_dec
y=0.05.to_dec + 0.01.to_dec
x==y
=> true
z=0.02.to_dec+0.04.to_dec
y==z
=> true
z==y
=> true
x==z
=> true
z==x
=> true

wants to have; e.g. it violates

‘from x==y follows x+z==y+z’

x+z == y+z
=> true
(x+z).to_s == (y+z).to_s
=> true
puts (x+z)
0.12E0
=> nil

for example

In particular, you don’t have

‘from x==y follows x-y==0’

x==y
=> true
x-y == 0
=> true

for example

> x, y = 0.6, 6 * 0.1

> x.to_s == y.to_s # => true

> (x - y).to_s == 0.to_s # => false

x, y = 0.6.to_dec, 6 * 0.1.to_dec
(x - y) == 0
=> true
(x - y) == 0.to_dec
=> true
(x - y).to_s == 0.to_dec.to_s
=> true

kind regards -botp

From: Michael U. [mailto:[email protected]]

However (among other problems):

> x = (1.0/3.0).to_dec

foul… ^^^^^^^^^ is a float op and not a bigdecimal/to_dec. use atomic
operations comparison, pls :slight_smile:

> y = 1.0.to_dec / 3.0.to_dec

that is more like it

> x == y # => false

of course.

so consider eg,

irb(main):057:0> x = 1.0.to_dec / 3.0.to_dec
=> #<BigDecimal:b7d87acc,‘0.3333333333 333333E0’,16(24)>
irb(main):058:0> y = 2.0.to_dec / 6.0.to_dec
=> #<BigDecimal:b7d7d874,‘0.3333333333 333333E0’,16(24)>
irb(main):059:0> x == y
=> true

kind regards -botp

Peña wrote:

From: Michael U. [mailto:[email protected]]

However (among other problems):

> x = (1.0/3.0).to_dec

foul… ^^^^^^^^^ is a float op and not a bigdecimal/to_dec. use atomic operations comparison, pls :slight_smile:

–snip–

What about

x = 1.0.to_dec / 3.0.to_dec
y = x + x + x
y == 1.0.to_dec # => false

?

HTH,

Michael


Michael U.
R&D Team
ISIS Information Systems Austria
tel: +43 2236 27551-542, fax: +43 2236 21081
e-mail: [email protected]
Visit our Website: www.isis-papyrus.com