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

Louis LaBrunda Lou at Keystone-Software.com
Fri Mar 29 14:33:23 UTC 2013


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