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

Chris Muller asqueaker at gmail.com
Sat Mar 30 21:04:59 UTC 2013


It also is good for remembering presentation must be able to be
distinct from internal representation.

On Fri, Mar 29, 2013 at 1:41 PM, Nicolas Cellier
<nicolas.cellier.aka.nice at gmail.com> wrote:
> 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
>>
>>
>
>
>
>


More information about the Squeak-dev mailing list