[squeak-dev] The joys (or not) of floating point numbers

Nicolas Cellier nicolas.cellier.aka.nice at gmail.com
Fri Mar 29 18:41:34 UTC 2013


Well, that's what I'm fighting against: looking good.
The good looking float are putting plenty of traps on our path, and it's
better to know about it.
I say that it's impossible to maintain the illusion of good looking and
friendly float, and in that case it's better to tell the awfull truth.
The changes in Squeak/Pharo are here to un-learn what we thought about
decimal numbers, or more accurately to learn that these rules don't apply
exactly to Float.

Beside, in most languages, if you round the float/double 0.995 to 2 digits
- either by a printf or another operation like round(0.995 , 2) - you'll
get 0.99 not 1.00.

Nicolas



2013/3/29 Louis LaBrunda <Lou at keystone-software.com>

> Hi Nicolas,
>
> Sorry to be getting back to this so late.  I take back what I said about
> asTrueFraction.  I'm sure it is fine.  But I think I question its use in
> this case.
>
> This all started when Stéphane said this:
>
>         0.995 printShowingDecimalPlaces: 2   ==>  '1.00'
>
> was wrong and I didn't think so.
>
> When one uses a method like printShowingDecimalPlaces: one expects (maybe
> there is some other similar method that one should expect this) the result
> to look good (yes that is very subjective) but I would say looking good is
> more important than being accurate.  Yes, that is an admission that
> asTrueFraction is the accurate way to convert floats.  Looking good would
> mean rounded nicely.
>
> Maybe I'm expecting printShowingDecimalPlaces: to be something it isn't
> intended to be but I don't see any other similar method that would perform
> the pretty way.
>
> Lou
>
> On Tue, 26 Mar 2013 22:11:41 +0100, Nicolas Cellier
> <nicolas.cellier.aka.nice at gmail.com> wrote:
>
> >2013/3/26 Louis LaBrunda <Lou at keystone-software.com>:
> >> Hi Nicolas,
> >>
> >> snip...
> >>
> >>>> In VA Smalltalk:
> >>>>
> >>>> 0.995 = 0.995s3 => true
> >>>> 0.995 < 0.995s3 => false
> >>
> >>>This is a default st80 behavior which converts minimumGenerality
> >>>number to maximumGenerality.
> >>
> >> In VA when one converts 0.995 to decimal, one gets 0.995s15, which when
> >> compared to 0.995s3 is equal.  And when 0.995s3 is converted to float
> that
> >> float compares equal to 0.995.
> >>
> >>>Maybe some of these conventions are hardwired in VM, I don't know, but
> >>>in other dialects it's handled at image side.
> >>
> >>>The idea was this one: if you perform an operation between an inexact
> >>>value and an exact value, the result is inexact.
> >>>So Float + * / - Integer,Fraction,ScaledDecimal will result into a
> Float.
> >>>Thus Float got a higher generality.
> >>
> >> This would imply that one should convert the ScaledDecimal to a Float
> >> before the compare.  But as above that would not change things (I think
> >> maybe because VA uses 8 byte floats).
> >>
> >> This is from Squeak:
> >>
> >> 0.995s3 asFloat
> >>  0.995
> >>
> >> 0.995s3 asFloat = 0.995
> >>  true
> >>
> >> 0.995 asScaledDecimal
> >>  0.99499999s8
> >>
> >> (995 / 1000) asScaledDecimal
> >>  0.995s3
> >>
> >> 0.995 asTrueFraction asScaledDecimal
> >>  0.99499999999999999555910790149937383830547332763671875s53
> >>
> >> (0.995 * 1000000000) asScaledDecimal / 1000000000
> >>  0.99500000s8
> >>
> >
> >Yep, but (0.995 * 1000000000) is inexact...
> >You can check that (0.995 * 1000000000) ~~ (0.995 asFraction * 1000000000)
> >
> >You cannot rely on results of any such inexact calculus, or you'll be
> >building on sand.
> >
> >> 0.995 * 1000000000
> >>  9.95e8
> >>
> >> Sorry, but I have run out of time to play at the moment.  So, I will
> just
> >> throw a thought out there.  I think there may be a problem with
> >> asTrueFraction.  Which if implemented differently might not make 0.995 <
> >> 0.995s3.
> >>
> >
> >Well I doubt, but I'm all ears ;)
> >
> >(0.995 asFraction storeStringBase: 2)
> >->
> '(2r11111110101110000101000111101011100001010001111010111/2r100000000000000000000000000000000000000000000000000000)'.
> >
> >(0.995 successor) asFraction storeStringBase: 2
> >->
> '(2r11111110101110000101000111101011100001010001111011/2r100000000000000000000000000000000000000000000000000)'
> >->
> '(2r11111110101110000101000111101011100001010001111011000/2r100000000000000000000000000000000000000000000000000000)'.
> >
> >(0.995 predecessor) asFraction storeStringBase: 2
> >->
> '(2r1111111010111000010100011110101110000101000111101011/2r10000000000000000000000000000000000000000000000000000)'
> >->
> '(2r11111110101110000101000111101011100001010001111010110/2r100000000000000000000000000000000000000000000000000000)'.
> >
> >0.995 ulp asFraction storeStringBase: 2
> >-> '(2r1/2r100000000000000000000000000000000000000000000000000000)'.
> >
> >(995/1000 - 0.995 asFraction) / 0.995 ulp
> >-> 0.04.
> >
> >(995/1000 - 0.995 successor asFraction) / 0.995 ulp
> >->  -0.96.
> >
> >(995/1000 - 0.995 successor asFraction) / 0.995 ulp
> >->  1.04.
> >
> >and numerator/denominator have this property:
> >
> >2r11111110101110000101000111101011100001010001111010111 highBit
> >-> 53.
> >2r100000000000000000000000000000000000000000000000000000 highBit
> >-> 54.
> >
> >So, 53 bits of significand, a power of two on denominator, that sounds
> >like a correct Float, slightly smaller than 1.
> >Next significand and previous significand are both further of
> >(995/1000) than 0.995 is.
> >0.995 is closest Float to 995/1000 and is smaller than 995/1000, no doubt.
> >
> >Nicolas
> >
> >>>The idea behind Squeak change is that every Float has an exact value
> >>>(asTrueFraction).
> >>>Since we have only two possible answers true/false for comparison, and
> >>>no maybe or dontKnow, it makes sense to compare the exact value.
> >>>This reduces the number of paradoxal equalities
> >>>
> >>>| a b c |
> >>>a := 1 << Float precision.
> >>>b := a + 1.
> >>>c := a asFloat.
> >>>{
> >>>c = b.
> >>>c = a.
> >>>a = b.
> >>>}
> >>>
> >>>In VW and VA, the first two are true, the last is false, which
> >>>suggests that = is not an equivalence relation.
> >>>In Squeak, only the second one is true.
> >>>
> >>>Same with inequalities, we expect (a < b) & (a = c) ==> (c < b) etc...
> >>>This is still true in Squeak, not in VA/VW/st80.
> >>>I think gst and Dolphin and maybe stx adopted Squeak behavior (to be
> confirmed).
> >>>
> >>>
> >>>> I think in VA Smalltalk there is some VM magic going on that makes the
> >>>> above work the way it does.  The #= and #< of Float are implemented
> in a
> >>>> primitive.  I guess when converting the '0.995' string to a float a
> little
> >>>> is lost and that would make it less than 0.995s3 but there is a lot
> of code
> >>>> floating around (sorry about the puns) to make floats look better.
>  In that
> >>>> case I would think people would want (0.995
> printShowingDecimalPlaces: 2)
> >>>> to show '1.00' and not '0.99'.
> >>>>
> >>>
> >>>No, people should not rely on such behavior with Floats, because
> >>>sooner than later they will be bitten.
> >>>We cannot cheat very long with this kind of assumptions.
> >>>My POV is that it is better to educate about false expectations with
> >>>Float, and that's the main benefit of (1/10) ~= 0.1.
> >>>
> >>>I would add that such print policy is not the behaviour of every other
> >>>language I know of.
> >>>
> >>>printf( "%.2f",0.995)
> >>>-> 0.99
> >>>
> >>>Because libm are carefully written nowdays and other languages
> >>>libraries are either built over libm or much more careful than
> >>>Smalltalk were (that mostly means more recent).
> >>>
> >>>> Anyway, we should try not to use floats without a very, very good
> reason.
> >>>> Like we have to send them outside of Smalltalk or we really need the
> speed
> >>>> or decimals and fractions take up too much memory.  But then we must
> live
> >>>> with their inaccuracies and display mess.
> >>>>
> >>>
> >>>Good advice, let's put those expectations on decimal fractions
> >>>(ScaledDecimal/FixedPoint) or general Fraction.
> >>>
> >>>> I have an untested theory that fractions can be close in speed to
> floats
> >>>> because divisions (that are expensive) can be pushed to the end of a
> >>>> computation because with fractions they are multiplies.
> >>>>
> >>>> Lou
> >>>
> >>>Why not, but huge numerators and denominators are not cheap compared
> >>>to Float operations.
> >>>OK reducing the fraction is the expensive part, but how does the cost
> >>>grow versus the length of operands?
> >>>
> >>>Also some geometric operations are not even possible on Q (hypot) so
> >>>you might soon need AlgebraicNumber.
> >>>And Smalltalk is also about graphics and geometry.
> >>>
> >>>Nicolas
> >>>
> >>>> -----------------------------------------------------------
> >>>> Louis LaBrunda
> >>>> Keystone Software Corp.
> >>>> SkypeMe callto://PhotonDemon
> >>>> mailto:Lou at Keystone-Software.com http://www.Keystone-Software.com
> >>>>
> >>>>
> >>>
> >> -----------------------------------------------------------
> >> Louis LaBrunda
> >> Keystone Software Corp.
> >> SkypeMe callto://PhotonDemon
> >> mailto:Lou at Keystone-Software.com http://www.Keystone-Software.com
> >>
> >>
> >
> -----------------------------------------------------------
> Louis LaBrunda
> Keystone Software Corp.
> SkypeMe callto://PhotonDemon
> mailto:Lou at Keystone-Software.com http://www.Keystone-Software.com
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20130329/ebfc1e2b/attachment.htm


More information about the Squeak-dev mailing list