Forum: Ruby Force calculation result to BigDecimal

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.
89316fb47af779e33e9d36d08a5eddd6?d=identicon&s=25 Robert Brown (rob99)
on 2007-07-17 01:34
Hi all,

I'm doing some complex math which I need to be based on BigDecimal
accuracy (not Float).  It seems that ruby defaults to Float when there's
a mixture of type in there, e.g.

Code :

   1. irb(main):001:0> require 'bigdecimal'
   2. => true
   3. irb(main):002:0> a = 5.123
   4. => 5.123
   5. irb(main):003:0> b = BigDecimal("6.789")
   6. => #<BigDecimal:8b4c0,'0.6789E1',8(8)>
   7. irb(main):004:0> c = a * b
   8. => 34.780047
   9. irb(main):005:0> c.class
  10. => Float

Is there a way I can force a BigDecimal outcome.

One way I guess would work is to make sure all args in the calculation
are BigDecimal, but that's a bit tiresome.  For a start, I'd have to
type check each arg and convert to BigDecimal if it was not BigDecimal -
that's gotta be slow...

On a related note, how do I initalize a BigDecimal from a Float (without
converting to string first).  How could I extend BigDecimal for this?


Thanks,



Rob
289cf19aa581c445915c072bf45c5e25?d=identicon&s=25 Todd Benson (Guest)
on 2007-07-17 03:18
(Received via mailing list)
On 7/16/07, Robert Brown <rob99brown@yahoo.com> wrote:
>    3. irb(main):002:0> a = 5.123
> One way I guess would work is to make sure all args in the calculation
>
>
> Rob

Here's a quick fix.  There may be a better way to handle this, though.
 Again, I'll give the usual warning about modifying core classes: you
shouldn't normally do it unless you can be certain it won't break
yours or anyone else's code.

class Float
  require 'bigdecimal'
  def big
    BigDecimal self.to_s
  end
end

p pi = Math::PI.big
#  prints out something like
# #<BigDecimal:2df3ffc,'0.3141592653 58979E1',20(20))
p "\n"

p pi * 2.0.big
# prints out something like
# #<BigDecimal:2deb35c,'0.6283185307 17958E1',20(36))
p "\n"

Todd
89316fb47af779e33e9d36d08a5eddd6?d=identicon&s=25 Robert Brown (rob99)
on 2007-07-17 07:50
> Here's a quick fix.  There may be a better way to handle this, though.
>  Again, I'll give the usual warning about modifying core classes: you
> shouldn't normally do it unless you can be certain it won't break
> yours or anyone else's code.

Thanks Todd...

What are the risks in modifying core classes?  I've already done this a
little by adding .round(n) to BigDecimal, where n is the decimal places.

Appreciate your tip, but I was thinking the other way round: have
BigDecimal take Float as an initializer, not have Float output
BigDecimal.

But this is all by-the-by.  What I'm really looking for is BigDecimal
math, not Float, in my calculations (when there's a mixture of
BigDecimal, Float and Fixnum types).

Still looking for suggestions on how I can do this better than manual
type-checking and casting...


Thanks,
C06869c119472a139eb163b72040b0db?d=identicon&s=25 Bertram Scharpf (Guest)
on 2007-07-17 09:09
(Received via mailing list)
Hi,

Am Dienstag, 17. Jul 2007, 14:50:18 +0900 schrieb Robert Brown:
> Appreciate your tip, but I was thinking the other way round: have
> BigDecimal take Float as an initializer, not have Float output
> BigDecimal.

-From time to time I wonder why there is no BigDecimal
initializer taking a Float argument. There seems to be a
point as Float values aren't unambiguous and you have to
explicitly mention which one you mean of

  "%f" % 1.8       #  => "1.800000"
  "%32.24f" % 1.8  #  => "      1.800000000000000044408921"

So I'd rather say it's disencouraged to convert Float to
BigDecimal.

Bertram
89316fb47af779e33e9d36d08a5eddd6?d=identicon&s=25 Robert Brown (rob99)
on 2007-07-17 09:30
Bertram Scharpf wrote:
> So I'd rather say it's disencouraged to convert Float to
> BigDecimal.

Indeed!  I'd rather not use Float at all in this project as it's
financial math and float quirks will cause me problems.  I'd be prepared
to take the performance hit and just turn off Float if I could...
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-07-17 09:31
(Received via mailing list)
2007/7/17, Todd Benson <caduceass@gmail.com>:
> >    2. => true
> >
> >
> >
> >
> > Rob
>
> Here's a quick fix.  There may be a better way to handle this, though.

Hm....  I don't think this is a fix because once you have a float you
lost all the precision.  A better fix is probably to change #coerce to
work properly. Another solution is to make sure all values are
converted to BD before starting the calculation.

Note, that BigDecimal is returned when working with integers.

>  Again, I'll give the usual warning about modifying core classes: you
> shouldn't normally do it unless you can be certain it won't break
> yours or anyone else's code.

Very good point!

Kind regards

robert
289cf19aa581c445915c072bf45c5e25?d=identicon&s=25 Todd Benson (Guest)
on 2007-07-17 14:14
(Received via mailing list)
On 7/17/07, Robert Klemme <shortcutter@googlemail.com> wrote:

> Hm....  I don't think this is a fix because once you have a float you
> lost all the precision.

Can you elaborate on this?  I was under the impression that converting
to BigDecimal before arithmetic operations would get around this.
Using
class Float
  def big; BigDecimal(self.to_s); end
end
irb > Math::PI.big * 2.0
=> 6.283185307178
irb > Math::PI.big * 2.0.big
=> #<BigDecimal:81c2b88,'0.6283185307 17958E1',20(36)>

> A better fix is probably to change #coerce to
> work properly. Another solution is to make sure all values are
> converted to BD before starting the calculation.

This would be the best solution (maybe through #induced_from?), but I
don't know how to do it.

> Note, that BigDecimal is returned when working with integers.

Todd
D0338c0de4cb3c5c17300396159933d1?d=identicon&s=25 Axel Etzold (Guest)
on 2007-07-17 15:04
(Received via mailing list)
Dear Robert,

> Indeed!  I'd rather not use Float at all in this project as it's
> financial math and float quirks will cause me problems.  I'd be prepared
> to take the performance hit and just turn off Float if I could...

you could use a "continued fractions" approximation

http://mathworld.wolfram.com/ContinuedFraction.html

to your Float number and then work with fractions without
any further loss in accuracy. Continued fractions are in a sense the
best possible approximations to irrational numbers
(and of course to rational numbers, the latter being
fractions themselves).

Once you have a fraction (use the code below to get it
from a continued fraction), you can calculate with it in Ruby
using Rational:

require "rational"
a=Rational(1,2)
b=Rational(3,4)
p a+b  # => Rational (5,4) , as 5/4 == 1/2+3/4
p (a+b).to_f  # => 1.25

Below is some code to give you the continued fraction representation
(4) in the webpage cited above.

From the continued fraction representation, you can obtain the
partial quotients mentioned in (11) of the cited webpage  by the method
cont_fract_to_fract below.


Best regards,

Axel


----------------
class Float
  def cont_fract_appr(*prec)
    a=[self.floor]
    t=[self-self.floor]
    res=[]
    if prec==[]
      prec=10**-8
    else
      prec=prec[0]
    end
    k=1
    rem=t[-1]
    while rem.abs>prec
      r_new=1/t[-1]
      if (r_new+prec).floor>r_new.floor
        r_new=r_new+prec
      end
      a[k]=r_new.floor
      t[k]=r_new-r_new.floor
      k=k+1
      p,q=(a.cont_fract_to_fract)
      rem=(self-p[-1].to_f/q[-1].to_f).abs
    end
    return a
  end
end

class Array
       def cont_fract_to_fract
               # produce the partial fractions (see eq. 11 of webpage)
    p=[0,1]
    q=[1,0]
    for n in 2..self.length+1
      p<<self[n-2]*p[-1]+p[-2]
      q<<self[n-2]*q[-1]+q[-2]
    end
    return p,q
  end
end
# usage example:


f=Math::PI
r=f.cont_fract_appr
p 'the continued fraction approximation (to precision 10**-8 is)'
p r
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-07-17 15:34
(Received via mailing list)
2007/7/17, Todd Benson <caduceass@gmail.com>:
> On 7/17/07, Robert Klemme <shortcutter@googlemail.com> wrote:
>
> > Hm....  I don't think this is a fix because once you have a float you
> > lost all the precision.
>
> Can you elaborate on this?  I was under the impression that converting
> to BigDecimal before arithmetic operations would get around this.

Actually you are right.  I somehow thought you intended #big to be
called on the result, i.e. after the calculation. Stupid me.  Sorry
for the confusion.

> > work properly. Another solution is to make sure all values are
> > converted to BD before starting the calculation.
>
> This would be the best solution (maybe through #induced_from?), but I
> don't know how to do it.

It would take me too much time ATM to do it and it should also be
checked whether changing #coerce causes issues somewhere else...

Kind regards

robert
This topic is locked and can not be replied to.