Path: csiph.com!usenet.pasdenom.info!weretis.net!feeder4.news.weretis.net!rt.uk.eu.org!newsfeed.xs4all.nl!newsfeed4a.news.xs4all.nl!xs4all!newsgate.cistron.nl!newsgate.news.xs4all.nl!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.035 X-Spam-Evidence: '*H*': 0.93; '*S*': 0.00; 'binary': 0.07; 'intermediate': 0.07; 'subject:bug': 0.07; 'earliest': 0.09; 'cc:addr:python-list': 0.11; 'python': 0.11; 'mostly': 0.14; 'decimal.': 0.16; 'digits.': 0.16; 'enough.': 0.16; 'from:addr:rosuav': 0.16; 'from:name:chris angelico': 0.16; 'lie': 0.16; 'oddity': 0.16; 'result;': 0.16; 'subject:python': 0.16; 'wrote:': 0.18; 'bit': 0.19; 'thu,': 0.19; '>>>': 0.22; 'import': 0.22; 'coding': 0.22; 'cc:addr:python.org': 0.22; 'error': 0.23; 'example.': 0.24; 'cc:2**0': 0.24; 'compare': 0.26; 'define': 0.26; 'nearly': 0.26; 'this:': 0.26; 'certain': 0.27; 'header:In- Reply-To:1': 0.27; 'point': 0.28; 'correct': 0.29; '[1]': 0.29; 'generally': 0.29; 'room': 0.29; 'message-id:@mail.gmail.com': 0.30; "d'aprano": 0.31; 'decimal': 0.31; 'steven': 0.31; 'up.': 0.33; 'problem': 0.35; "can't": 0.35; 'something': 0.35; 'but': 0.35; 'received:google.com': 0.35; 'doing': 0.36; 'subject:?': 0.36; 'should': 0.36; 'too': 0.37; 'two': 0.37; 'thank': 0.38; 'pm,': 0.38; 'little': 0.38; 'enough': 0.39; 'days': 0.60; 'easy': 0.60; 'middle': 0.60; 'mentioned': 0.61; 'numbers': 0.61; "you're": 0.61; 'back': 0.62; "you've": 0.63; 'sum': 0.64; 'more': 0.64; 'occur': 0.65; 'worth': 0.66; 'between': 0.67; 'close': 0.67; '26,': 0.68; 'guaranteed': 0.75; 'yourself': 0.78; 'avg': 0.84; 'multiplying': 0.84; 'to:none': 0.92; 'average': 0.93; 'room,': 0.93 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:cc :content-type; bh=T0sPAClmmQ55Ij8Hj3T7Jq5zJhJq1WWSk/Sgsz1sI2M=; b=TpCxIT1g2CqwmHAF6m1kYxuDSAKFhAka8VyUE8V7g60EQgThAoe0r7DowuOn1s7EAa EK2YjwpaOdFoHpjVbkFESxdAAUzTBVZUTRvSGYm4Dib1Lz1cIb90Z6yq/NsYCW67LrdD r4VQaJ49HTfW0r3olVmU036gQ13DNJqyj5YThfpl+mXzHxhH0ia2HWfWexOFXohIQjf3 uNLd99b7zwqIgEwHlFtyNYC4yX9ENO1PSNnCwa58AT1RfDbgzi7R0w5sALUCpoGpin4k Xq8jc+NINJWbl1k+8lDzWV/v65z6Dq2e1zMLN3kmd1DspQb0AQjm5uMH+Q+tBGY2H4J8 LOjw== MIME-Version: 1.0 X-Received: by 10.52.159.226 with SMTP id xf2mr10367463vdb.14.1403775525372; Thu, 26 Jun 2014 02:38:45 -0700 (PDT) In-Reply-To: <53abe4ad$0$11121$c3e8da3@news.astraweb.com> References: <645be4a7-2e1b-44a4-9c45-9184c6df5518@googlegroups.com> <53ab8bc4$0$11121$c3e8da3@news.astraweb.com> <53abe4ad$0$11121$c3e8da3@news.astraweb.com> Date: Thu, 26 Jun 2014 19:38:45 +1000 Subject: Re: python 3.44 float addition bug? From: Chris Angelico Cc: "python-list@python.org" Content-Type: text/plain; charset=UTF-8 X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 51 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1403775938 news.xs4all.nl 2948 [2001:888:2000:d::a6]:47456 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:73613 On Thu, Jun 26, 2014 at 7:15 PM, Steven D'Aprano wrote: > Here's an error that *cannot* occur with binary floats: the average of > two numbers x and y is not guaranteed to lie between x and y! > > > py> from decimal import * > py> getcontext().prec = 3 > py> x = Decimal('0.516') > py> y = Decimal('0.518') > py> (x + y) / 2 > Decimal('0.515') > > > Ouch! But what you're looking at is also a problem with intermediate rounding, as the sum of .516 and .518 can't be represented in 3 digits. One rule of thumb that I learned back in my earliest coding days was that your intermediate steps should have significantly more precision than your end result; so if you want an end result with a certain precision (say, 3 decimal digits), you should calculate with a bit more. Of course, "a bit" is nearly impossible to define [1], but if you're mostly adding and subtracting, or multiplying by smallish constants, 1-2 extra digits' worth of precision is generally enough. Or just give yourself lots of room, like using double-precision for something like the above example. Compare this: >>> from decimal import * >>> getcontext().prec = 4 >>> x = Decimal('0.516') >>> y = Decimal('0.519') >>> avg = (x + y) / 2 >>> getcontext().prec = 3 >>> avg + 0 Decimal('0.518') >>> (x + y) / 2 Decimal('0.52') Doing the intermediate calculation with precision 3 exhibits the same oddity Steven mentioned (only the other way around - result is too high), but having a little extra room in the middle means the result is as close to the correct answer as can be represented (0.517 would be equally correct). With floating point on an 80x87, you can do this with 80-bit FPU registers; I don't know of a way to do so with Python floats, but (obviously) it's pretty easy with Decimal. ChrisA [1] Thank you, smart-aleck up the back, I am fully aware that "a bit" is exactly one binary digit. That's not enough for a decimal float. You've made your point, now shut up. :)