Colin Law wrote in post #970536: > The more I think about it the more I think that the ruby > implementation of BigDecimal is flawed. BigDecimal('1') / > BigDecimal('3') should be stored as a rational (ie as two values, 1 > and 3 to be divided). Otherwise it seems to me that it is not 'proper' > BigDecimal. I have been unable to find documentation that fully > defines what the result of 1/3 should be.
I think differently here (but I may be mistaken ...). BigDecimal, for me, as the name implies, guarantees an "exact" representation of all numbers that are of the form: [+-] (d(n)*10^n + d(n-1)*10^(n-1) + ... + d(0)*10^0 + d(-1)*10^(-1) + ... d(-m)*10^(-m)) with d(n), the digit n places to the left of unity and d(-m), the digit m places to the right of the unit and the comma. The ^ (caret) represents the "to the power of" function (e.g. 10^3 == 1000). E.g. in the decimal number "3004.5006" d(3) = 3 d(2) = 0 d(1) = 0 d(0) = 4 d(-1) = 5 d(-2) = 0 d(-3) = 0 d(-4) = 6 >> a = BigDecimal.new("3004.5006") => #<BigDecimal:b72706c0,'0.30045006E4',8(12)> These numbers form a (mathematical) field for the operators: +, -, * , but NOT with the division operator. The nice thing about BigDecimal is that it will automatically expand it's precision if needed to make sure the representation remains correct when using the functions: +, -, *. There is always a finite (and exact) precision that is sufficient to represent the result of a [+-*] b Let's try it: >> a = BigDecimal.new("1000000000000000000",12) => #<BigDecimal:b725ae60,'0.1E19',4(24)> # (precision automatically larger than the 12 I requested) >> b = BigDecimal.new("0.00000000000000003", 12) => #<BigDecimal:b72320b4,'0.3E-16',4(24)> # (precision automatically larger than the 12 I requested) >> sum = a+b => #<BigDecimal:b722254c,'0.1000000000 0000000000 0000000000 000003E19',40(56)> # nice, precision is enlarged to represent the _exact_ result :-) >> difference = a-b => #<BigDecimal:b721ee9c,'0.9999999999 9999999999 9999999999 99997E18',40(56)> # nice, precision is enlarged to represent the _exact_ result :-) >> product = a*b => #<BigDecimal:b721c494,'0.3E2',4(16)> # nice, precision is reduced because more is not required :-) But for the division ... >> division = a/b => #<BigDecimal:b721aa7c,'0.3333333333 3333333333 3333333333 3333333333 3333333E35',48(56)> >> division = a/b BigDecimal gives it a shot, takes a "large" precision ... but in a "Decimal" representation (Sum of d(i).10^i), there is no way to represent this exactly. So there is no limited value of precision that will allow the exact representation. Otherwise said, the function division '/' is not part of the field, it can produce results that are outside the set of decimals (even if the 2 operands are inside). The Decimal numbers are clearly a subset of the Rational numbers. It would be quite feasible to make a Rational class which is a field with the operators '+', '-', '*', '/'. Of course, the Rational class exists and does this neatly: >> ra = Rational(1000000000000000000000000000,1) => Rational(1000000000000000000000000000, 1) >> rb = Rational(2,1000000000000000000000000000) => Rational(1, 500000000000000000000000000) >> ra.to_s => "1000000000000000000000000000" >> rb.to_s => "1/500000000000000000000000000" >> (ra+rb).to_s => "500000000000000000000000000000000000000000000000000001/500000000000000000000000000" >> (ra-rb).to_s => "499999999999999999999999999999999999999999999999999999/500000000000000000000000000" >> (ra*rb).to_s => "2" >> (ra/rb).to_s => "500000000000000000000000000000000000000000000000000000" >> (rb/ra).to_s => "1/500000000000000000000000000000000000000000000000000000" >> ra.inspect => "Rational(1000000000000000000000000000, 1)" >> rb.inspect => "Rational(1, 500000000000000000000000000)" But then again ... sin, sqrt, etc. are not part of the field and will bring the result outside of the BigRational set into the set of "Real" numbers. My conclusion: * on the *, +, - operators, it is nice to see BigDecimal expand the precision of the results to stick to an "exact" representation and stay in the field. * if the BigDecimal class sticks to its definition of "Decimal" (and not Rational) then behavior of BigDecimal on the division operator can be nothing else then arbitrary. Possible solutions I see on top of my head: A do nothing (the result of division has a "large" precision, but is not "exact") B raise an exception if the representation becomes not exact C set an instance variable in the BigDecimal object that marks it is "not exact" (like the "tainted" flag) D let BigDecimal return a Rational if the result can not be exactly represented as a BigDecimal E use the Rational class if you want exact divisions ... They all have their advantages: * A OK. Understanding the behaviour is enough. I understand now that +.-,* work as expected and / causes the risk to loose exact representation. * B (could be Class or per object flag) ==> in bookkeeping applications, exactness is required, so any calculation that destroys the exactness must be flagged. So if you calculate the "average" profit over your 7 divisions, you would get an exception, unless you did something special and understand the risks * C this is really unintrusive and allows one to check at the end if the exactness did not get lost along the road * E this exists, use it if you want an "exact" representation of 1/3. I am not in favor of D because it silently changes the class of the result and this violates the principle of least surprise. If you want Rational, use it from the start then. HTH, Peter -- Posted via http://www.ruby-forum.com/. -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-t...@googlegroups.com. To unsubscribe from this group, send email to rubyonrails-talk+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.