# Force calculation result to BigDecimal

**URL:** <https://www.ruby-forum.com/t/force-calculation-result-to-bigdecimal/109579>\
**Category:** Ruby\
**Created:** [July 17, 2007, 1:34am UTC](https://www.ruby-forum.com/t/force-calculation-result-to-bigdecimal/109579 "2007-07-17T01:34:28Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![rjberry](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/34f0e0/32.png) [@rjberry](https://www.ruby-forum.com/u/rjberry)\
**Post date:** [July 17, 2007, 1:34am UTC](https://www.ruby-forum.com/t/force-calculation-result-to-bigdecimal/109579/1 "2007-07-17T01:34:28Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![rjberry](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/34f0e0/32.png) [@rjberry](https://www.ruby-forum.com/u/rjberry)\
**Post date:** [July 17, 2007, 7:50am UTC](https://www.ruby-forum.com/t/force-calculation-result-to-bigdecimal/109579/2 "2007-07-17T07:50:02Z")

</div>

> 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,

---

<div class="post-metadata">

**Author:** ![rjberry](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/34f0e0/32.png) [@rjberry](https://www.ruby-forum.com/u/rjberry)\
**Post date:** [July 17, 2007, 9:09am UTC](https://www.ruby-forum.com/t/force-calculation-result-to-bigdecimal/109579/3 "2007-07-17T09:09:07Z")

</div>

Hi,

Am Dienstag, 17. Jul 2007, 14:50:18 +0900 schrieb Robert B.:

> 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

---

<div class="post-metadata">

**Author:** ![rjberry](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/34f0e0/32.png) [@rjberry](https://www.ruby-forum.com/u/rjberry)\
**Post date:** [July 17, 2007, 9:30am UTC](https://www.ruby-forum.com/t/force-calculation-result-to-bigdecimal/109579/4 "2007-07-17T09:30:01Z")

</div>

Bertram S. 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…

---

<div class="post-metadata">

**Author:** ![rjberry](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/34f0e0/32.png) [@rjberry](https://www.ruby-forum.com/u/rjberry)\
**Post date:** [July 17, 2007, 3:18am UTC](https://www.ruby-forum.com/t/force-calculation-result-to-bigdecimal/109579/5 "2007-07-17T03:18:39Z")

</div>

On 7/16/07, Robert B. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

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

---

<div class="post-metadata">

**Author:** ![rjberry](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/34f0e0/32.png) [@rjberry](https://www.ruby-forum.com/u/rjberry)\
**Post date:** [July 17, 2007, 9:31am UTC](https://www.ruby-forum.com/t/force-calculation-result-to-bigdecimal/109579/6 "2007-07-17T09:31:12Z")

</div>

2007/7/17, Todd B. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> > 1. =\> 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

---

<div class="post-metadata">

**Author:** ![rjberry](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/34f0e0/32.png) [@rjberry](https://www.ruby-forum.com/u/rjberry)\
**Post date:** [July 17, 2007, 2:14pm UTC](https://www.ruby-forum.com/t/force-calculation-result-to-bigdecimal/109579/7 "2007-07-17T14:14:10Z")

</div>

On 7/17/07, Robert K. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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

---

<div class="post-metadata">

**Author:** ![rjberry](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/34f0e0/32.png) [@rjberry](https://www.ruby-forum.com/u/rjberry)\
**Post date:** [July 17, 2007, 3:34pm UTC](https://www.ruby-forum.com/t/force-calculation-result-to-bigdecimal/109579/8 "2007-07-17T15:34:22Z")

</div>

2007/7/17, Todd B. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> On 7/17/07, Robert K. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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

---

<div class="post-metadata">

**Author:** ![rjberry](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/34f0e0/32.png) [@rjberry](https://www.ruby-forum.com/u/rjberry)\
**Post date:** [July 17, 2007, 3:04pm UTC](https://www.ruby-forum.com/t/force-calculation-result-to-bigdecimal/109579/9 "2007-07-17T15:04:15Z")

</div>

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

> **[Continued Fraction -- from Wolfram MathWorld](https://mathworld.wolfram.com/ContinuedFraction.html)**
>
> The term "continued fraction" is used to refer to a class of expressions of which generalized continued fraction of the form b\_0+(a\_1)/(b\_1+(a\_2)/(b\_2+(a\_3)/(b\_3+...))) =b\_0+K\_(n=1)^infty(a\_n)/(b\_n) (and the terms may be integers, reals,...

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
