OK.  I apologize in advance.  Soapbox on...

I, for one, like Ruby's current approach to numeric type conversions.
There is an automatic promotion from Fixnum to Bignum to prevent  overflow.
(a nice feature borrowed from SmallTalk)

However, as others have pointed out, there is no analogous promotion between
discrete, countable Integer types and Floats.  This is because all Floats
are *approximations* of abstract, continuous Real numbers.  

Floats are imprecise by design.  They are intended to represent measured
quantities, not countable objects.  Any real valued measurement is likely to
have more error in its underlying physics than the 8-byte binary floating
point format Ruby uses to approximate that value.

Floats should not be used for counting *anything* discrete.  Not pennies,
not array elements, not machine states.  (ok, maybe pennies if you are
dealing with current stock prices :-)

Sure, they can approximate 1.0 fairly well.  But, 1.1 is an inexact
repeating binary fraction.

A variant of the float to string conversion that tries to preserve all the
binary information would certainly be useful, but not as the default to_s
method.  That is, unless you think that:

irb>  2.1 - 3.0
-0.8999999999999999

would not be "surprising"!  (well, at least annoying)

Then, of course, there remains the question of efficiency.  Many simple
iterative numerical algorithms will cause BigDecimal and Rational numbers to
balloon in bytes required as they struggle to maintain perfect accuracy. 
And, as they become bigger in memory, they will also become slower.  Think
of numerical integration, for example,  or most any sort of successive
approximation, in general.

But, theoretical niceties aside, most plain folks are surprised when their
fancy computer says:

   2.1 - 3.0  != -0.9

What would fix this problem while still preserving efficiency and keep us
numerical nerds happy?

One very elegant fix would be to invent a DecimalFloat type.  It would have
limited precision, as Floats do, but 0.1 would be represented precisely, as
we finger counting humans expect it to be.  I've never seen this implemented
in any other language.  It would be a cool experiment.

What most numerical methods folks do in practice, is to define equality of
floats over an epsilon interval that is context dependent.  So, if you
insist on counting dollars with Floats, any values equal to within 1/2 a
penny (or 0.005 dollars) might be considered "equal" for all practical
purposes.  Still, it's a pain to have to specify the epsilon all the time. 
Here's one way to avoid that:

require 'delegate'
class Money < DelegateClass(Float)
  Epsilon = 0.005
  
  def <=> other
    diff = self - other
    return -1 if diff > Epsilon
    return 1 if diff < -Epsilon
    0
  end
  
  def initialize dollars
    super dollars.to_f
  end
end

def Money euro
  Money.new euro
end
====

And, here's a suboptimal way to monkey patch Float with a "dynamic" epsilon
that should behave as plain folks expect in most contexts:

class Float
  def <=> other
    epsilon = self * 1e-14
    diff = self - other
    return -1 if diff > epsilon
    return 1 if diff < -epsilon
    0
  end

  def == other  #because built-in Float#== bypasses <=>
    (self<=>other) == 0
  end
end

Note however, that this "dynamic epsilon" can be overcome by accumulating
round off errors over
many calculations.  But, by then, it's not as obvious as 2.1 - 3.0  != -0.9,
so most folks don't notice.

- brent


Roger Pack wrote:
> 
>>> There is public-domain C code that formats floating-point values as the
>>> shortest string that parses back to the exact same value.  The only
>>> thing that needs to happen is to interface Ruby with that code.
> 
> Is that code good enough or should we fix it at "x" digits?  I'd
> probably prefer the code that formats it as the shortest string
> possible [?]
> 
> ...(snip)...
> 
> I think the surprising part of it for me is that we have a  Fixnum ->
> Bignum auto conversion "when a normal int would bite us" [like on
> overflow] but do not have that same privilege with regard to the
> floating point realm.  No auto conversion.  The bite remains.
> 
> That being said, I think the fear is that BigDecimal will be so very
> slow that it would be too much of a performance hit.  As in probably
> extremely slow.
> 
> The question then is which of these two unsultry choices you'd
> prefer....well for me I would prefer the one that makes me worry less,
> which would be BigDecimal :)
> 
> If the hit is deemed too hard though, I suppose the better formatting
> at least helps us track down the bugs when they happen, if not avoid
> them.
> Thoughts?
> -=r
> 
> 
> 

-- 
View this message in context: http://www.nabble.com/-ruby-core%3A22325--suggestions-for-float-tp22144150p22303851.html
Sent from the ruby-core mailing list archive at Nabble.com.