<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><div>On Jun 1, 2015, at 10:24, Nicholas Chammas <<a href="mailto:nicholas.chammas@gmail.com">nicholas.chammas@gmail.com</a>> wrote:</div><div><br></div><blockquote type="cite"><div><div dir="ltr"><div class="markdown-here-wrapper" style=""><p style="margin:0px 0px 1.2em!important">Well, I learned a lot about decimals today. :)</p>
<p style="margin:0px 0px 1.2em!important">On Mon, Jun 1, 2015 at 3:08 AM, Nick Coghlan <a href="http://mailto:ncoghlan@gmail.com">ncoghlan@gmail.com</a> wrote:</p>
<blockquote style="margin:1.2em 0px;border-left-width:4px;border-left-style:solid;border-left-color:rgb(221,221,221);padding:0px 1em;color:rgb(119,119,119);quotes:none">
<p style="margin:0px 0px 1.2em!important">In a world of binary computers, no programming language is free of<br>those constraints - if you choose decimal literals as your default,<br>you take a <em>big</em> performance hit, because computers are designed as<br>binary systems. (Some languages, like IBM’s REXX, do choose to use<br>decimal integers by default)</p>
</blockquote>
<p style="margin:0px 0px 1.2em!important">I guess it’s a non-trivial tradeoff. But I would lean towards considering people likely to be affected by the performance hit as doing something “not common”. Like, if they are doing that many calculations that it matters, perhaps it makes sense to ask them to explicitly ask for floats vs. decimals, in exchange for giving the majority who wouldn’t notice a performance difference a better user experience.</p>
<p style="margin:0px 0px 1.2em!important">On Mon, Jun 1, 2015 at 10:58 AM, Steven D’Aprano <a href="http://mailto:steve@pearwood.info">steve@pearwood.info</a> wrote:</p>
<blockquote style="margin:1.2em 0px;border-left-width:4px;border-left-style:solid;border-left-color:rgb(221,221,221);padding:0px 1em;color:rgb(119,119,119);quotes:none">
<p style="margin:0px 0px 1.2em!important">I wish this myth about Decimals would die, because it isn’t true.</p>
</blockquote>
<p style="margin:0px 0px 1.2em!important">Your email had a lot of interesting information about decimals that would make a good blog post, actually. Writing one up will perhaps help kill this myth in the long run :)</p>
<blockquote style="margin:1.2em 0px;border-left-width:4px;border-left-style:solid;border-left-color:rgb(221,221,221);padding:0px 1em;color:rgb(119,119,119);quotes:none">
<p style="margin:0px 0px 1.2em!important">In the past, I’ve found that people are very resistant to this fact, so<br>I’m going to show a few examples of how Decimals violate the fundamental<br>laws of mathematics just as floats do.</p>
</blockquote>
<p style="margin:0px 0px 1.2em!important">How many of your examples are inherent limitations of decimals vs. problems that can be improved upon?</p>
<p style="margin:0px 0px 1.2em!important">Admittedly, the only place where I’ve played with decimals extensively is on Microsoft’s SQL Server (where they are the <a href="https://msdn.microsoft.com/en-us/library/ms179899.aspx">default literal</a>). I’ve stumbled in the past on <a href="http://dba.stackexchange.com/q/18997/2660">my own decimal gotchas</a>, but looking at your examples and trying them on SQL Server I suspect that most of the problems you show are problems of precision and scale.</p>
<p style="margin:0px 0px 1.2em!important">Perhaps Python needs better rules for how precision and scale are affected by calculations (<a href="https://msdn.microsoft.com/en-us/library/ms190476.aspx">here are SQL Server’s</a>, for example), or better defaults when they are not specified?</p>
<p style="margin:0px 0px 1.2em!important">Anyway, here’s what happens on SQL Server for some of the examples you provided.</p>
<p style="margin:0px 0px 1.2em!important">Adding 100:</p>
<blockquote style="margin:1.2em 0px;border-left-width:4px;border-left-style:solid;border-left-color:rgb(221,221,221);padding:0px 1em;color:rgb(119,119,119);quotes:none">
<p style="margin:0px 0px 1.2em!important">py> from decimal import Decimal as D<br>py> x = D(10)**30<br>py> x == x + 100  # should be False<br>True</p>
</blockquote>
<pre style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;font-size:1em;line-height:1.2em;margin:1.2em 0px"><code class="hljs language-sql" style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:pre-wrap;border:1px solid rgb(234,234,234);border-radius:3px;display:inline;background-color:rgb(248,248,248);white-space:pre;overflow:auto;border-radius:3px;border:1px solid rgb(204,204,204);padding:0.5em 0.7em;display:block!important;display:block;overflow-x:auto;padding:0.5em;color:rgb(51,51,51);background:rgb(248,248,248)"><span class="hljs-operator"><span class="hljs-keyword" style="color:rgb(51,51,51);font-weight:bold">DECLARE</span> @x <span class="hljs-built_in" style="color:rgb(0,134,179)">DECIMAL</span>(<span class="hljs-number" style="color:rgb(0,128,128)">38</span>,<span class="hljs-number" style="color:rgb(0,128,128)">0</span>) = <span class="hljs-string" style="color:rgb(221,17,68)">'1'</span> + <span class="hljs-keyword" style="color:rgb(51,51,51);font-weight:bold">REPLICATE</span>(<span class="hljs-number" style="color:rgb(0,128,128)">0</span>, <span class="hljs-number" style="color:rgb(0,128,128)">30</span>);</span>

IF @x = @x + 100
  <span class="hljs-operator"><span class="hljs-keyword" style="color:rgb(51,51,51);font-weight:bold">SELECT</span> <span class="hljs-string" style="color:rgb(221,17,68)">'equal'</span> <span class="hljs-keyword" style="color:rgb(51,51,51);font-weight:bold">AS</span> adding_100
<span class="hljs-keyword" style="color:rgb(51,51,51);font-weight:bold">ELSE</span>
  <span class="hljs-keyword" style="color:rgb(51,51,51);font-weight:bold">SELECT</span> <span class="hljs-string" style="color:rgb(221,17,68)">'not equal'</span> <span class="hljs-keyword" style="color:rgb(51,51,51);font-weight:bold">AS</span> adding_100</span>
</code></pre>
<p style="margin:0px 0px 1.2em!important">Gives <a href="http://sqlfiddle.com/#!6/9eecb7db59d16c80417c72d1/1645/0">“not equal”</a>. Leaving out the precision when declaring <code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:pre-wrap;border:1px solid rgb(234,234,234);border-radius:3px;display:inline;background-color:rgb(248,248,248)">@x</code> (i.e. going with the <a href="https://msdn.microsoft.com/en-us/library/ms187746.aspx">default precision of 18</a>) immediately yields an understandable data truncation error.</p></div></div></div></blockquote><div>Obviously if you know the maximum precision needed before you start and explicitly set it to something big enough (or 7 places bigger than needed) you won't have any problem. <span style="background-color: rgba(255, 255, 255, 0);">Steven chose a low precision just to make the problems easy to see and understand; he could just as easily have constructed examples for a precision of 18.</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">Unfortunately, even in cases where it is both possible and sufficiently efficient to work out and set the precision high enough to make all of your calculations exact, that's not something most people know how to do reliably. In the fully general case, it's as hard as calculating error propagation.</span></div><div><br></div><div>As for the error: Python's decimal flags that too; the difference is that the flag is ignored by default. You can change it to warn or error instead. Maybe the solution is to make that easier--possibly just changing the docs. If you read the whole thing you will eventually learn that the default context ignores most such errors, but a one-liner gets you a different context that acts like SQL Server, but who reads the whole module docs (especially when they already believe they understand how decimal arithmetic works)? Maybe moving that up near the top would be useful?</div><div><br></div></body></html>