Forum: Ruby about Float

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Cda3d26c2566666a0f507073b1389628?d=identicon&s=25 Kyung won Cheon (kdream95)
on 2008-12-17 02:39
a = 1333.3339999999964
b = 1333.3339999999965
c = 1333.334

puts a       # 1333.334
puts b       # 1333.334
puts c       # 1333.334

puts a == b  # => false
puts a == c  # => false
puts b == c  # => false

#########################################################
# HELP ME^^
#
# How can I see 'real value' of Float object(a, b, c) ?
#########################################################
Cfb541839e42dec39f23a8a4ab8eb762?d=identicon&s=25 Christophe Mckeon (polypus)
on 2008-12-17 02:47
> # How can I see 'real value' of Float object(a, b, c) ?

>> sprintf("%.3f", 1.6804985609845)
=> "1.680"
>> sprintf("%.8f", 1.6804985609845)
=> "1.68049856"

cheers,
_c
5316d692cc603c3ad2df0f03869bb96b?d=identicon&s=25 Michael Morin (Guest)
on 2008-12-19 04:02
(Received via mailing list)
Kyung won Cheon wrote:
> puts b == c  # => false
>
> #########################################################
> # HELP ME^^
> #
> # How can I see 'real value' of Float object(a, b, c) ?
> #########################################################

Comparing floating point numbers is usually not a very good idea.
They're only approximations of the real number you're trying to
represent.  For that reason, when printing floating point numbers, it's
useful to specify with what precision you want to print them.  This
usually involves some sort of format string.  The sprintf example works,
but I prefer something like this.

a = 6.232523
puts ("%.3f" % a)
31e038e4e9330f6c75ccfd1fca8010ee?d=identicon&s=25 Gregory Brown (Guest)
on 2008-12-19 07:14
(Received via mailing list)
On Tue, Dec 16, 2008 at 8:32 PM, Kyung won Cheon <kdream95@gmerce.co.kr>
wrote:

> #########################################################
> # HELP ME^^

http://docs.sun.com/source/806-3568/ncg_goldberg.html
E7559e558ececa67c40f452483b9ac8c?d=identicon&s=25 Gary Wright (Guest)
on 2008-12-19 19:17
(Received via mailing list)
On Dec 19, 2008, at 1:05 AM, Gregory Brown wrote:

> On Tue, Dec 16, 2008 at 8:32 PM, Kyung won Cheon <kdream95@gmerce.co.kr
> > wrote:
>
>> #########################################################
>> # HELP ME^^
>
> http://docs.sun.com/source/806-3568/ncg_goldberg.html

That is the document I usually send people to also.

I think the critical insight to understand is that floating point
literals written in decimal notation can not be exactly represented
internally as a binary value. Since there is no one-to-one
correspondence between the two representations even the simplest
expression can introduce rounding or approximation errors.

The real number (1/10) has a finite decimal representation but an
infinite binary representation so:

   puts 0.1

requires that the string '0.1' be converted to an approximate floating
point representation of (1/10) and then that representation be
converted back to decimal number for output.  You get different
results depending on how much precision you ask for in those
conversions:

 >> (1..50).each { |p| puts "%0.*f" % [p, 0.1] }
0.1
0.10
0.100
0.1000
0.10000
0.100000
0.1000000
0.10000000
0.100000000
0.1000000000
0.10000000000
0.100000000000
0.1000000000000
0.10000000000000
0.100000000000000
0.1000000000000000
0.10000000000000001
0.100000000000000006
0.1000000000000000056
0.10000000000000000555
0.100000000000000005551
0.1000000000000000055511
0.10000000000000000555112
0.100000000000000005551115
0.1000000000000000055511151
0.10000000000000000555111512
0.100000000000000005551115123
0.1000000000000000055511151231
0.10000000000000000555111512313
0.100000000000000005551115123126
0.1000000000000000055511151231258
0.10000000000000000555111512312578
0.100000000000000005551115123125783
0.1000000000000000055511151231257827
0.10000000000000000555111512312578270
0.100000000000000005551115123125782702
0.1000000000000000055511151231257827021
0.10000000000000000555111512312578270212
0.100000000000000005551115123125782702118
0.1000000000000000055511151231257827021182
0.10000000000000000555111512312578270211816
0.100000000000000005551115123125782702118158
0.1000000000000000055511151231257827021181583
0.10000000000000000555111512312578270211815834
0.100000000000000005551115123125782702118158340
0.1000000000000000055511151231257827021181583405
0.10000000000000000555111512312578270211815834045
0.100000000000000005551115123125782702118158340454
0.1000000000000000055511151231257827021181583404541
0.10000000000000000555111512312578270211815834045410
Cfb541839e42dec39f23a8a4ab8eb762?d=identicon&s=25 Christophe Mckeon (polypus)
on 2008-12-19 20:59
and just in case you need further convincing, this is the scariest
example i've ever seen. taken from Stefano Taschini's
http://intervals.rubyforge.org/

Take into consideration the rather innocent looking function

  def f(x,y)
    (333.75-x**2)* y**6 + x**2 * (11* x**2 * y**2-121 * y**4 -2) +
      5.5 * y**8 + x/(2*y)
  end

We can calculate it for some specific x and y,

  f(77617.0,33096.0)          # => 1.17260394005318

There is only one problem: this result is WRONG. The correct result can
be obtained by calculating separately the numerator and denominator of f
using integer arithmetic.

  def f_num(x,y)
    ((33375- 100 * x**2)* y**6 +
      100 * x**2 * (11* x**2 * y**2-121 * y**4 -2) +
      550 * y**8) *
    2*y + 100 *x
  end

  def f_den(x,y)
    200*y
  end

  f_num(77617, 33096).to_f / f_den(77617, 33096).to_f
                              # => -0.827396059946821

_c
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2008-12-20 12:37
I get the correct answer using bc too:

$ bc
scale=100
x=77617
y=33096
t1=(333.75-x*x)*y*y*y*y*y*y
t2=x*x*(11*x*x*y*y - 121*y*y*y*y - 2)
t3=5.5*y*y*y*y*y*y*y*y
t4=x/(2*y)
t1+t2+t3+t4
-.827396059946821368141165095479816291999033115784384819917814841672\
7096930142615421803239062122310854

However, using BigDecimal, the answer is remarkably different:

  require "bigdecimal"
  puts f(BigDecimal.new("77167.0"),BigDecimal.new("33096.0"))
  # => 9.15359360631475e+34

That's a sign plus 35 orders of magnitude different :-)
26a81e5badb9e002ab9ed3542036e584?d=identicon&s=25 Michael Libby (Guest)
on 2008-12-20 15:04
(Received via mailing list)
On Sat, Dec 20, 2008 at 5:29 AM, Brian Candler <b.candler@pobox.com>
wrote:

> However, using BigDecimal, the answer is remarkably different:
>
>  require "bigdecimal"
>  puts f(BigDecimal.new("77167.0"),BigDecimal.new("33096.0"))
>  # => 9.15359360631475e+34
>
> That's a sign plus 35 orders of magnitude different :-)

When I use BigDecimal all the way through and keep things clean in
terms of groupings (explicit parens around *everything*), I get what
appears to be the correct answer:

require 'bigdecimal'

SCALE = 100

AAA = BigDecimal.new("333.75", SCALE)
BBB = BigDecimal.new("11.0", SCALE)
CCC = BigDecimal.new("121.0", SCALE)
DDD = BigDecimal.new("2.0", SCALE)
EEE = BigDecimal.new("5.5", SCALE)

def fun(x,y)
  puts  ( (AAA - (x**2)) * (y**6) ) +
        ( (x**2) * ((BBB * (x**2) * (y**2)) - (CCC * (y**4)) - DDD )) +
        ( EEE * (y**8) ) +
        ( x / (DDD * y) )
end

fun(BigDecimal.new("77617.0", SCALE), BigDecimal.new("33096.0", SCALE))



Gives:

-0.8273960599468213681411650954798162919990331157843848199178148416727096930142615421803239062122310853275320280396422528402224E0

Is there something I'm missing? Seems like it would be pretty safe to
use BigDecimal in this case.

 -Michael
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2008-12-20 17:11
You're right, it works if you explicitly promote some of the constants
to BigDecimal:

  class Numeric
    def bd
      BigDecimal.new(to_s,100)
    end
  end

  def f(x,y)
    (333.75.bd-x**2)* y**6 + x**2 * (11 * x**2 * y**2-121 * y**4 - 2) +
      5.5.bd * y**8 + x/(2*y)
  end

  puts f(77617.bd,33096.bd)

I didn't change any parentheses, just added .bd at certain places.

The trouble seems to be: mixing BigDecimal and Integer works fine
(promotes to BigDecimal), but mixing BigDecimal with Float downgrades to
Float.

irb(main):007:0> BigDecimal.new("7",100) + 2
=> #<BigDecimal:b7f07910,'0.9E1',4(12)>
irb(main):008:0> 2 + BigDecimal.new("7",100)
=> #<BigDecimal:b7f02b68,'0.9E1',4(12)>
irb(main):009:0> BigDecimal.new("7",100) + 2.0
=> 9.0
irb(main):010:0> (BigDecimal.new("7",100) + 2.0).class
=> Float

So you have to be careful not to mix any Float constants in.
Dccbfc183eb4b61b94480cc06f36f450?d=identicon&s=25 Steven D'Aprano (Guest)
on 2008-12-24 09:35
(Received via mailing list)
On Fri, 19 Dec 2008 14:51:29 -0500, Christophe Mckeon wrote:

> and just in case you need further convincing, this is the scariest
> example i've ever seen. taken from Stefano Taschini's
> http://intervals.rubyforge.org/

The example you give is pretty scary, but there's much scarier. You
don't
need a big complicated function to hit weird floating point anomalies.

x + y - y should always equal x, right?

irb(main):012:0> 1.0 + 3.0 - 3.0
=> 1.0

Seems to work. Except when it doesn't...

irb(main):017:0> x = 1.0/3
=> 0.333333333333333
irb(main):018:0> x + 0.1 - 0.1 == x
=> false


> Take into consideration the rather innocent looking function
>
>   def f(x,y)
>     (333.75-x**2)* y**6 + x**2 * (11* x**2 * y**2-121 * y**4 -2) +
>       5.5 * y**8 + x/(2*y)
>   end

You've obviously lived a depraved life if you think that looks
innocent :-)


> We can calculate it for some specific x and y,
>
>   f(77617.0,33096.0)          # => 1.17260394005318
>
> There is only one problem: this result is WRONG. The correct result can
> be obtained by calculating separately the numerator and denominator of f
> using integer arithmetic.

Out of curiosity, I tried this equation in Python, and got the same
result. Hardly surprising, as Python and Ruby will probably be using the
same floating point library.

I also tried in on my HP-48GX calculator, and got the same result too.
If
arithmetic truths could be voted on, that would be three votes for the
wrong answer :)

Does anyone have access to Mathematica? What does it give?


>   f_num(77617, 33096).to_f / f_den(77617, 33096).to_f
>                               # => -0.827396059946821


Just to add more confusion to the story, here's another mathematically
equivalent expression (unless I've made a silly mistake):

irb(main):026:0> def f2(x, y)
irb(main):027:1> a = x**2
irb(main):028:1> b = y**2
irb(main):029:1> f = x/(2*y)
irb(main):030:1> 11*a*b*(a - 11*b)+(333.75 - a)*b**3 + 5.5*b**4 - 2*a+f
irb(main):031:1> end
=> nil
irb(main):032:0> f2(77617, 33096)
=> -12048797377.0


The moral of the story? Floats are evil. They are just similar enough to
the real numbers you learn about in school to fool you into thinking
that
they behave just like reals, but they don't.
This topic is locked and can not be replied to.