Why doesn't Python solve the "bug" Rounding (round)?

Source: Internet
Author: User


Reply content:


Because binary floating-point numbers do not solve this problem.

First look at a phenomenon that is unrelated to round:
 
<code class="language-python"><span class="o">>>></span> <span class="k">def</span> <span class="nf">show</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>

<span class="o">...</span>     <span class="s">"""打印一个数,20 位精度"""</span>

<span class="o">...</span>     <span class="k">print</span><span class="p">(</span><span class="s">'{:.20f}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>

<span class="o">...</span>

<span class="o">>>></span> <span class="n">show</span><span class="p">(</span><span class="mf">1.5</span><span class="p">)</span>

<span class="mf">1.50000000000000000000</span>

<span class="o">>>></span> <span class="n">show</span><span class="p">(</span><span class="mf">1.25</span><span class="p">)</span>

<span class="mf">1.25000000000000000000</span>

<span class="o">>>></span> <span class="n">show</span><span class="p">(</span><span class="mf">1.245</span><span class="p">)</span>

<span class="mf">1.24500000000000010658</span>

<span class="o">>>></span> <span class="n">show</span><span class="p">(</span><span class="mf">1.45</span><span class="p">)</span>

<span class="mf">1.44999999999999995559</span>

<span class="o">>>></span> <span class="n">show</span><span class="p">(</span><span class="mf">1.415</span><span class="p">)</span>

<span class="mf">1.41500000000000003553</span>

</code>
Rounding is based on decimal, and there is an error when the binary cannot be accurately represented.
Any place where decimal arithmetic is required requires a decimal. Decimal replaces float:
 
<code class="language-python"><span class="o">>>></span> <span class="n">Decimal</span><span class="p">(</span><span class="mf">1.45</span><span class="p">)</span>

<span class="n">Decimal</span><span class="p">(</span><span class="s">'1.4499999999999999555910790149937383830547332763671875'</span><span class="p">)</span>

<span class="o">>>></span> <span class="n">Decimal</span><span class="p">(</span><span class="s">'1.45'</span><span class="p">)</span>

<span class="n">Decimal</span><span class="p">(</span><span class="s">'1.45'</span><span class="p">)</span>

<span class="o">>>></span> <span class="n">Context</span><span class="p">(</span><span class="n">prec</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">rounding</span><span class="o">=</span><span class="n">ROUND_HALF_UP</span><span class="p">)</span><span class="o">.</span><span class="n">create_decimal</span><span class="p">(</span><span class="s">'1.45'</span><span class="p">)</span>

<span class="n">Decimal</span><span class="p">(</span><span class="s">'1.5'</span><span class="p">)</span>

<span class="o">>>></span> <span class="n">Decimal</span><span class="p">(</span><span class="s">'1.45'</span><span class="p">)</span><span class="o">.</span><span class="n">normalize</span><span class="p">(</span><span class="n">Context</span><span class="p">(</span><span class="n">prec</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">rounding</span><span class="o">=</span><span class="n">ROUND_HALF_UP</span><span class="p">))</span>

<span class="n">Decimal</span><span class="p">(</span><span class="s">'1.5'</span><span class="p">)</span>

<span class="o">>>></span> <span class="n">Decimal</span><span class="p">(</span><span class="n">Decimal</span><span class="p">(</span><span class="s">'1.45'</span><span class="p">)</span><span class="o">.</span><span class="n">quantize</span><span class="p">(</span><span class="n">Decimal</span><span class="p">(</span><span class="s">'.1'</span><span class="p">),</span> <span class="n">rounding</span><span class="o">=</span><span class="n">ROUND_HALF_UP</span><span class="p">))</span>

<span class="n">Decimal</span><span class="p">(</span><span class="s">'1.5'</span><span class="p">)</span>

</code>
Note that this is in the very nature of binary floating-point:this are not a bug in Python, and it's not a bug in Y Our code either. You'll see the same kind of thing in all languages so support your hardware ' s floating-point arithmetic (although some l Anguages may not be display the difference by default, or with all output modes).
    • Some decimal numbers (such as 0.1) cannot be accurately represented within the machine with a limited number of bits 0 and 1, and can only be increased by increasing the number of fixed digits, thus approximating the original decimal number.

Round ( number [, ndigits])
Return The floating point value number rounded to ndigits digits after the decimal point. If ndigits is omitted, it defaults to zero. The result is a floating point number. Values is rounded to the closest multiple of ten to the power minus ndigits; If the multiples is equally close, the rounding is do away from 0.
    • The round function of Python is defined as taking the number closest to numbers in any integer *10^ (-ndigits), and if there are two integers that are equal, the value is taken from one side (negative -0-positive) away from 0.
round(0.5) is 1.0 and round(-0.5) is -1.0)
The main test has nothing to do with that bug. Round is a four-shed six into 50% pairs. The official implementation of Python does have a problem, as to why the author does not improve, the original author may be what mentality, see Liu's answer. The general mentality may be: Anyway this error is not me, so although there are methods, but I will not solve the problem.

At first glance, many people will interpret the problem as an inexact floating-point number. The floating-point numbers are imprecise, which is common sense and is right.

But floating-point numbers are not inaccurate in all cases, and are not " as long as the floating-point number is not accurate, the related computational problems involved have no solution to the value」。

The round problem of Python is a typical example, and it is speculated that the author's point of view is that there is no need to solve the problem of floating-point accuracy. Like @ Liu's point of view, so it leads to the present result.

However, careful analysis reveals that 0.5 of such floating-point numbers can be accurately represented, and the specificity of the round function is that the accuracy of round rounding is meaningless. So round the inaccuracy of the problem itself can be solved.

For the inaccuracy of the round, it is important that the result is in one or the end. I've written a simple example to illustrate the problem, and of course this function is not accurate in certain situations, but the correct result can be obtained in the case of the problem.

 
<code class="language-c"><span class="cp">#include <stdio.h></stdio.h></span>

 

<span class="kt">float</span> <span class="nf">my_round</span><span class="p">(</span><span class="kt">float</span> <span class="n">src</span><span class="p">,</span> <span class="kt">int</span> <span class="n">idx</span><span class="p">)</span>

<span class="p">{</span>

    <span class="kt">int</span> <span class="n">i</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="n">i</span><span class="o">=</span><span class="n">idx</span><span class="p">;</span><span class="n">i</span><span class="o">--</span><span class="p">;)</span>

        <span class="n">src</span> <span class="o">*=</span><span class="mi">10</span><span class="p">;</span>

    <span class="kt">float</span> <span class="n">dest</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">src</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">src</span> <span class="o">>=</span> <span class="n">dest</span><span class="o">+</span><span class="mf">0.5</span><span class="p">)</span>

        <span class="n">dest</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="n">i</span><span class="o">=</span><span class="n">idx</span><span class="p">;</span><span class="n">i</span><span class="o">--</span><span class="p">;)</span>

        <span class="n">dest</span> <span class="o">/=</span><span class="mi">10</span><span class="p">;</span>

    <span class="k">return</span> <span class="n">dest</span><span class="p">;</span>

<span class="p">}</span>

 

 

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>

<span class="p">{</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"result=%f</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">my_round</span><span class="p">(</span><span class="mf">1.5</span><span class="p">,</span> <span class="mi">0</span><span class="p">));</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"result=%f</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">my_round</span><span class="p">(</span><span class="mf">1.25</span><span class="p">,</span> <span class="mi">1</span><span class="p">));</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"result=%f</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">my_round</span><span class="p">(</span><span class="mf">1.245</span><span class="p">,</span> <span class="mi">2</span><span class="p">));</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"result=%f</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">my_round</span><span class="p">(</span><span class="mf">1.45</span><span class="p">,</span> <span class="mi">1</span><span class="p">));</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"result=%f</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">my_round</span><span class="p">(</span><span class="mf">1.415</span><span class="p">,</span> <span class="mi">2</span><span class="p">));</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"result=%f</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">my_round</span><span class="p">(</span><span class="mf">2.675</span><span class="p">,</span> <span class="mi">2</span><span class="p">));</span>

<span class="p">}</span>

Two questions, one is

The actual storage of floating point numbers in the computer (here is the storage in Python)


<code class="language-text">expect:  1.0

actual:  1

expect:  1.1

actual:  1.100000000000000088817841970012523233890533447265625

expect:  1.2

actual:  1.1999999999999999555910790149937383830547332763671875

expect:  1.3

actual:  1.3000000000000000444089209850062616169452667236328125

expect:  1.4

actual:  1.399999999999999911182158029987476766109466552734375

expect:  1.5

actual:  1.5

expect:  1.6

actual:  1.600000000000000088817841970012523233890533447265625

expect:  1.7

actual:  1.6999999999999999555910790149937383830547332763671875

expect:  1.8

actual:  1.8000000000000000444089209850062616169452667236328125

expect:  1.9

actual:  1.899999999999999911182158029987476766109466552734375

</code>
Very simple, because 2.675 may be 2.6749 at the time of presentation, so round is still 2.67. The essence of your demand is: the exact decimal operation. However, float is not designed to meet this demand, and decimal is. Therefore, it is difficult to customize a single round for float, which does not conform to the design intent of float. Take your function as an example, temp*10 this operation is not accurate under float.

>>> 1.222*10
12.219999999999999 I mean, why not write a function that solves this problem: for example (just one example, probably from performance ah kind of certainly not, official for what reason does not write a similar)
def myround (par,l):
temp = 1
For I in range (L):
temp*=10
v = Int ((par+0.5/temp) *temp)/Temp
Return V

i = 1.25
Print (Myround (i,1))
i = 1.245
Print (Myround (i,2))
i = 1.21
Print (Myround (i,1))
i = 1.249
Print (Myround (i,2))

----
1.3
1.25
1.2
1.25 His explanation should be that this is not a bug. The default values are float,float, so Python should be accurate only for decimal. I think it's quite normal.
  • Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.