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