Re: Question about floating point

2018-09-01 Thread Paul Moore
On Sat, 1 Sep 2018 at 12:31, Frank Millman  wrote:
>
> "Frank Millman"  wrote in message news:pm3l2m$kv4$1...@blaine.gmane.org...
> >
> > I know about this gotcha -
> >
> > >>> x = 1.1 + 2.2
> > >>> x
> > 3.3003
> >
> [...]
>
> I have enjoyed the discussion, and I have learnt a lot about floating point.
> Thanks to all.
>
> I have just noticed one oddity which I thought worth a mention.
>
> >>> from decimal import Decimal as D
> >>> f"{D('1.1')+D('2.2'):.60f}"
> '3.3000'
> >>> '{:.60f}'.format(D('1.1') + D('2.2'))
> '3.3000'
> >>> '%.60f' % (D('1.1') + D('2.2'))
> '3.2998223643160599749535322189331054687500'
> >>>
>
> The first two format methods behave as expected. The old-style '%' operator
> does not.
>
> Frank

Presumably, Decimal has a custom formatting method. The old-style %
formatting doesn't support custom per-class formatting, so %.60f
converts its argument to float and then prints it.

Paul
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-09-01 Thread Steven D'Aprano
On Sat, 01 Sep 2018 13:27:59 +0200, Frank Millman wrote:

 from decimal import Decimal as D
 f"{D('1.1')+D('2.2'):.60f}"
> '3.3000'
 '{:.60f}'.format(D('1.1') + D('2.2'))
> '3.3000'
 '%.60f' % (D('1.1') + D('2.2'))
> '3.2998223643160599749535322189331054687500'


> The first two format methods behave as expected. The old-style '%'
> operator does not.

The % operator casts the argument to a (binary) float. The other two 
don't need to, because they call Decimal's own format method.


-- 
Steven D'Aprano
"Ever since I learned about confirmation bias, I've been seeing
it everywhere." -- Jon Ronson

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-09-01 Thread Frank Millman

"Frank Millman"  wrote in message news:...

"Frank Millman"  wrote in message news:pm3l2m$kv4$1...@blaine.gmane.org...


I know about this gotcha -

>>> x = 1.1 + 2.2
>>> x
3.3003


[...]

I have enjoyed the discussion, and I have learnt a lot about floating point. 
Thanks to all.


I have just noticed one oddity which I thought worth a mention.


from decimal import Decimal as D
f"{D('1.1')+D('2.2'):.60f}"

'3.3000'

'{:.60f}'.format(D('1.1') + D('2.2'))

'3.3000'

'%.60f' % (D('1.1') + D('2.2'))

'3.2998223643160599749535322189331054687500'




The first two format methods behave as expected. The old-style '%' operator 
does not.


Frank






--
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-08-31 Thread Steven D'Aprano
On Fri, 31 Aug 2018 18:45:16 +1200, Gregory Ewing wrote:

> Steven D'Aprano wrote:
>> The right way is to
>> set the rounding mode at the start of your application, and then let
>> the Decimal type round each calculation that needs rounding.
> 
> It's not clear what you mean by "rounding mode" here. If you mean
> whether it's up/down/even/whatever, then yes, you can probably set that
> as a default and leave it.

I mean the rounding mode :-)

https://docs.python.org/3/library/decimal.html#rounding-modes


> However, as far as I can see, Decimal doesn't provide a way of setting a
> default number of decimal places to which results are rounded. You can
> set a default *precision*, but that's not the same thing.

Indeed it is not. That's a very good point, and I had completely 
forgotten about it! Thank you.

The quantize method is intended for the use-case we are discussing, to 
round values to a fixed number of decimal places. The Decimal FAQs 
mention that:

https://docs.python.org/3/library/decimal.html#decimal-faq

I think this is a good use-case for subclassing Decimal as a Money class.

[...]
> I don't think this is a bad thing, because often you don't want to use
> the same number of places for everything, For example, people dealing
> with high-volume low-value goods often calculate with unit prices having
> more than 2 decimal places. In those kinds of situations, you need to
> know exactly what you're doing every step of the way.

As opposed to anyone else calculating with money?




-- 
Steven D'Aprano
"Ever since I learned about confirmation bias, I've been seeing
it everywhere." -- Jon Ronson

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-08-30 Thread Gregory Ewing

Steven D'Aprano wrote:
The right way is to 
set the rounding mode at the start of your application, and then let the 
Decimal type round each calculation that needs rounding.


It's not clear what you mean by "rounding mode" here. If you
mean whether it's up/down/even/whatever, then yes, you can
probably set that as a default and leave it.

However, as far as I can see, Decimal doesn't provide a
way of setting a default number of decimal places to
which results are rounded. You can set a default *precision*,
but that's not the same thing. (Precision is the total
number of significant digits, not the number of digits
after the decimal point.)

So if you're working with dollars and cents and want all
your divisions rounded to 2 places, you're going to have
to do that explicitly each time.

I don't think this is a bad thing, because often you
don't want to use the same number of places for everything,
For example, people dealing with high-volume low-value goods
often calculate with unit prices having more than 2 decimal
places. In those kinds of situations, you need to know
exactly what you're doing every step of the way.

We have here a brilliant hammer specially designed for 
banging in just this sort of nail,


Except that we don't, we actually have an impact screwdriver,
so you've going to have to bring your hammer to deal with
nails properly. And a single size of hammer isn't going to
suit all kinds of nail.

--
Greg
--
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-08-30 Thread Steven D'Aprano
On Thu, 30 Aug 2018 19:22:29 +1200, Gregory Ewing wrote:

> Steven D'Aprano wrote:
>> Why in the name of all that's holy would anyone want to manually round
>> each and every intermediate calculation when they could use the Decimal
>> module and have it do it automatically?
> 
> I agree that Decimal is the safest and probably easiest way to go, but
> saying that it "does the rounding automatically" is a bit misleading.
> 
> If you're adding up dollars and cents in Decimal, no rounding is needed
> in the first place, because it represents whole numbers of cents exactly
> and adds them exactly.

"Round to exact" is still rounding :-P

I did already say that addition and subtraction was exact in Decimal. (I 
also mentioned multiplication, but that's wrong.)


> If you're doing something that doesn't result in a whole number of cents
> (e.g. calculating a unit price from a total price and a quantity) you'll
> need to think about how you want it rounded, and should probably include
> an explicit rounding step, if only for the benefit of someone else
> reading the code.

If you're not using Banker's Rounding for financial calculations, you're 
probably up to no good *wink*

Of course with Decimal you always have to option to round certain 
calculations by hand, if you have some specific need to. But in general, 
that's just annoying and error-prone book-keeping. The right way is to 
set the rounding mode at the start of your application, and then let the 
Decimal type round each calculation that needs rounding.

The whole point of Decimal, the reason it was invented, was to do this 
sort of thing. We have here a brilliant hammer specially designed for 
banging in just this sort of nail, and you're saying "Well, sure, but you 
probably want to bang it in with your elbow, if only for the benefit of 
onlookers..."

:-)


-- 
Steven D'Aprano
"Ever since I learned about confirmation bias, I've been seeing
it everywhere." -- Jon Ronson

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-08-30 Thread Gregory Ewing

Steven D'Aprano wrote:
Why in the name of all that's holy would anyone want to manually round 
each and every intermediate calculation when they could use the Decimal 
module and have it do it automatically?


I agree that Decimal is the safest and probably easiest way to
go, but saying that it "does the rounding automatically" is
a bit misleading.

If you're adding up dollars and cents in Decimal, no rounding
is needed in the first place, because it represents whole
numbers of cents exactly and adds them exactly.

If you're doing something that doesn't result in a whole
number of cents (e.g. calculating a unit price from a total
price and a quantity) you'll need to think about how you want it
rounded, and should probably include an explicit rounding step,
if only for the benefit of someone else reading the code.

--
Greg
--
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-08-29 Thread Steven D'Aprano
On Wed, 29 Aug 2018 11:31:29 +1200, Gregory Ewing wrote:

> Frank Millman wrote:
>> I have been trying to explain why
>> they should use the decimal module. They have had a counter-argument
>> from someone else who says they should just use the rounding technique
>> in my third example above.
> 
> It's possible to get away with this by judicious use of rounding.
> There's a business software package I work with that does this -- every
> number is rounded to a defined number of decimal places before being
> stored in its database, so the small inaccuracies resulting from inexact
> representations don't get a chance to accumulate.

This software package doesn't actually use the *10/10 trick, does it?

As an answer to the question, "Should I use this clever *10/10 trick?" 
I'm not sure it's relevant to say "Yep, sure, this package does something 
completely different and it works fine!" *wink*

> If you're going to do this, I would NOT recommend using the rounding
> technique in your example -- it seems to itself be relying on accidents
> of the arithmetic. Use the round() function:

Or better still, DON'T manually use the round function, let the 
interpreter do the rounding for you by using Decimal. That's what its for.

Why in the name of all that's holy would anyone want to manually round 
each and every intermediate calculation when they could use the Decimal 
module and have it do it automatically?



-- 
Steven D'Aprano
"Ever since I learned about confirmation bias, I've been seeing
it everywhere." -- Jon Ronson

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-08-29 Thread Steven D'Aprano
On Tue, 28 Aug 2018 16:47:25 +0200, Frank Millman wrote:

> The reason for my query is this. I am assisting someone with an
> application involving monetary values. I have been trying to explain why
> they should use the decimal module. They have had a counter-argument
> from someone else who says they should just use the rounding technique
> in my third example above.

*head-desk*

And this is why we can't have nice things.

Presumably this money application doesn't just work on hard-coded literal 
values, right? So this "programmer" your friend is listening to prefers 
to write this:

money = (a + b)*10/10

instead of:

money = a + b

presumably because programming isn't hard enough without superstitious 
ritual that doesn't actually solve the problem.

In the second case, you have (potentially) *one* rounding error, due to 
the addition.

In the first case, you get the *exact same rounding error* when you do 
(a+b). Then you get a second rounding error by multiplying by ten, and a 
third rounding error when you divide by ten.

Now its true that sometimes those rounding errors will cancel. You found 
an example:

py> (1.1 + 2.2)*10/10 == 3.3
True

but it took me four attempts to find a counter-example, where the errors 
don't cancel:

py> (49675.23 + 10492.95)*10/10 == 60168.18
False

To prove it isn't a fluke:

py> (731984.84 + 173.32)*10/10 == 732158.16
False

py> (170734.84 - 173.39)*10/10 == 170561.45
False


Given that it has three possible three rounding errors instead of one, it 
is even possible that this "clever trick" could end up being *worse* than 
just doing a single addition. But my care factor isn't high enough to 
track down an example (if one exists).

For nearly all applications involving money, one correct solution is to 
use either integer numbers of cents (or whatever the smallest currency 
you ever care about happens to be). Then all additions, subtractions and 
multiplications will be exact, without fail, and you only need to worry 
about rounding divisions. You can minimize (but not eliminate) that by 
calculating in tenths of a cent, which effectively gives you a guard 
digit.

Or, just use decimal, which is *designed* for monetary applications 
(among other things). You decide on how many decimal places to keep (say, 
two, or three if you want a guard digit), a rounding mode (Banker's 
Rounding is recommended for financial applications), and just do your 
calculations with no "clever tricks".

Add two numbers, then add tax:

money = (a+b)*(1+t/100)

compared to the "clever trick":

money = (a+b)*10/10 * (1 + t)*10/10


Which would you rather do?



-- 
Steven D'Aprano
"Ever since I learned about confirmation bias, I've been seeing
it everywhere." -- Jon Ronson

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-08-29 Thread Oscar Benjamin
On Tue, 28 Aug 2018 at 15:50, Frank Millman  wrote:
>
> "Frank Millman"  wrote in message news:pm3l2m$kv4$1...@blaine.gmane.org...
> >
> > I know about this gotcha -
> >
> > >>> x = 1.1 + 2.2
> > >>> x
> > 3.3003
> >
> [...]
> >
> > >>> y = 3.3
> > >>> y
> > 3.3
> >
> [...]
> >
> > >>> z = (1.1 + 2.2) * 10 / 10
> > >>> z
> > 3.3
>
> Thanks to Chris and Stephen for the replies.
>
> They were interesting, but actually did not answer the question that I
> forgot to ask!
>
> The reason for my query is this. I am assisting someone with an application
> involving monetary values. I have been trying to explain why they should use
> the decimal module. They have had a counter-argument from someone else who
> says they should just use the rounding technique in my third example above.
>
> My gut-feel says that they are wrong, but I don't have the knowledge to
> prove it. I think a lot depends on what they are going to do with this value
> down the line - maybe it is 'safe enough' for their purposes, so I don't
> want to overstate my case. Are there edge cases where that rounding method
> could fail?

That rounding method is not suitable. As mentioned by others you
should at least use round() if working with floats. Here's an example
that shows that it doesn't work in the way you want:

>>> 0.11 - 0.1
0.009995
>>> (0.11 - 0.1) * 10 / 10
0.009995
>>> round(0.11 - 0.1, 2)
0.01

I wouldn't use floats for this though.

--
Oscar
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-08-28 Thread Gregory Ewing

Frank Millman wrote:
I have been trying to explain why 
they should use the decimal module. They have had a counter-argument 
from someone else who says they should just use the rounding technique 
in my third example above.


It's possible to get away with this by judicious use of rounding.
There's a business software package I work with that does this --
every number is rounded to a defined number of decimal places
before being stored in its database, so the small inaccuracies
resulting from inexact representations don't get a chance to
accumulate.

If you're going to do this, I would NOT recommend using the
rounding technique in your example -- it seems to itself be
relying on accidents of the arithmetic. Use the round()
function:

>>> 1.1 + 2.2
3.3003
>>> round(1.1 + 2.2, 1)
3.3

Also, if you go this route, always keep in mind *why* it works --
it relies on the actual value always being close enough to
a multiple of a power of 10 that it rounds to the correct
value when converted to decimal.


Are there edge cases where that rounding method could fail?


Yes, it's easy to come up with examples that make it fail,
e.g. multiplying by a large number, or adding up a few billion
monetary amounts before rounding. These things are very
unlikely to happen in an accounting application, but they
are theoretically possible.

So, you can probably make it work if you're careful, but for
peace of mind I would still recommend using Decimal, because
Python has it, and it eliminates all of these potential
problems.

--
Greg
--
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-08-28 Thread Rob Gaddi

On 08/28/2018 07:11 AM, Frank Millman wrote:

Hi all

I know about this gotcha -


x = 1.1 + 2.2
x

3.3003

According to the docs, the reason is that "numbers like 1.1 and 2.2 do 
not have exact representations in binary floating point."


So when I do this -


y = 3.3
y

3.3

what exactly is happening? What is 'y' at this point?

Or if I do this -


z = (1.1 + 2.2) * 10 / 10
z

3.3

What makes it different from the first example?

Thanks

Frank Millman



https://en.wikipedia.org/wiki/IEEE_754

Python uses what the C folks call doubles, and what IEEE calls a 
binary64.  If you look down at the precision chart, you'll see that for 
a value V, you have precision limits on the order of (V * 1e-16).  If 
you strip off the 3.3 that you wanted from the result that you got, your 
error there is 3e-16, and all that's rounded for display, so all you can 
really talk about is order of magnitude.


--
Rob Gaddi, Highland Technology -- www.highlandtechnology.com
Email address domain is currently out of order.  See above to fix.
--
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-08-28 Thread MRAB

On 2018-08-28 15:11, Frank Millman wrote:

Hi all

I know about this gotcha -


x = 1.1 + 2.2
x

3.3003

According to the docs, the reason is that "numbers like 1.1 and 2.2 do not
have exact representations in binary floating point."

So when I do this -


y = 3.3
y

3.3

what exactly is happening? What is 'y' at this point?

Or if I do this -


z = (1.1 + 2.2) * 10 / 10
z

3.3

What makes it different from the first example?


There's a bit of rounding going on in the last few digits. For example:

>>> 1.1
1.1
>>> _ - 1
0.10009
>>> 0.1
0.1
>>> _ * 10 - 1
0.0
--
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-08-28 Thread Chris Angelico
On Wed, Aug 29, 2018 at 12:47 AM, Frank Millman  wrote:
> They were interesting, but actually did not answer the question that I
> forgot to ask!
>
> The reason for my query is this. I am assisting someone with an application
> involving monetary values. I have been trying to explain why they should use
> the decimal module. They have had a counter-argument from someone else who
> says they should just use the rounding technique in my third example above.
>
> My gut-feel says that they are wrong, but I don't have the knowledge to
> prove it. I think a lot depends on what they are going to do with this value
> down the line - maybe it is 'safe enough' for their purposes, so I don't
> want to overstate my case. Are there edge cases where that rounding method
> could fail?

For virtually all situations, the correct solution is neither float
nor decimal; the correct way to handle money is an integer, storing a
number of cents, yen, pence, or whatever your smallest unit is. In the
rare situations where you need fractional cents, you probably need to
use decimal.Decimal(), but if you're doing that sort of thing (eg
forex), you'll know what your requirements are far better than I do.
Everywhere else, integers make way better sense than anything else.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-08-28 Thread Frank Millman

"Frank Millman"  wrote in message news:pm3l2m$kv4$1...@blaine.gmane.org...


I know about this gotcha -

>>> x = 1.1 + 2.2
>>> x
3.3003


[...]


>>> y = 3.3
>>> y
3.3


[...]


>>> z = (1.1 + 2.2) * 10 / 10
>>> z
3.3


Thanks to Chris and Stephen for the replies.

They were interesting, but actually did not answer the question that I 
forgot to ask!


The reason for my query is this. I am assisting someone with an application 
involving monetary values. I have been trying to explain why they should use 
the decimal module. They have had a counter-argument from someone else who 
says they should just use the rounding technique in my third example above.


My gut-feel says that they are wrong, but I don't have the knowledge to 
prove it. I think a lot depends on what they are going to do with this value 
down the line - maybe it is 'safe enough' for their purposes, so I don't 
want to overstate my case. Are there edge cases where that rounding method 
could fail?


Frank


--
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-08-28 Thread Grant Edwards
On 2018-08-28, Frank Millman  wrote:

 x = 1.1 + 2.2
 x
> 3.3003
>
> According to the docs, the reason is that "numbers like 1.1 and 2.2 do not 
> have exact representations in binary floating point."

Right.

> So when I do this -
>
 y = 3.3
 y
> 3.3
>
> what exactly is happening?

By default, Python shows only a certain number of significant digits
(17?), and the decimal value of y rounded to 17 places is 3.3.

> What is 'y' at this point?

If you want to see the exact value:

>>> y = 3.3
>>> y.hex()
'0x1.ap+1'

Or in decimal, you can request more significant digits than default:

>>> '%0.60f' % y
'3.2998223643160599749535322189331054687500'

> Or if I do this -
>
 z = (1.1 + 2.2) * 10 / 10
 z
> 3.3

>>> z = (1.1 + 2.2) * 10 / 10
>>> '%0.60f' % y
'3.3002664535259100375697016716003417968750'
>>> z.hex()
'0x1.ap+1'


>>> y = 1.1 + 2.2
>>> '%0.60f' % z
'3.2998223643160599749535322189331054687500'
>>> y.hex()
'0x1.a6667p+1'

As you can see from the hex values, they differ by one (least
significant) bit in the significand.

> What makes it different from the first example?

The additional multiplication and division operations are not exact,
so "* 10 / 10" produces a result that's slightly different than the
one you started with.

Two things to remember:

1. The actual numbers in the computer are in base-2.  The mapping
   between what you see in base-10 and the real values in base-2
   _is_always_exact_.

2. The _operations_ (multiplicaton/division/addition/subtraction)
   _are_not_always_exact_.  Even if you start with values that map
   exactly from base-10 to base-2 (some do) doing operations on them
   may not produce an exact result.

-- 
Grant Edwards   grant.b.edwardsYow! Spreading peanut
  at   butter reminds me of
  gmail.comopera!!  I wonder why?

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-08-28 Thread Stephen Tucker
Hi Frank,

One difference is that, in order to carry out the instructions embodied in
the first example, the computer has to perform arithmetic. And it adds the
binary approximation of 1.1 (which is very slightly wrong) to the binary
approximation of 2.2 (which, again, is very slightly wrong). It then prints
the denary equivalent to the sum of those two which happens to be very
slightly more than 3.3.

When you type 3.3, it stores that as the binary approximation of 3.3 (which
is y at that stage, to answer your question, and is, again, no surprise,
also very slightly wrong) and then prints the denary equivalent of that
binary approximation which happens to end up sufficiently close to 3.3 so
as to cause it to print 3.3.

I hope that helps.

The experts around here (of which I am not one) may well point you to the
decimal  package which allows better handling of such arithmetic, if you
are want the computer to behave more like you would want it to behave.

Regards,

Stephen Tucker.








On Tue, Aug 28, 2018 at 3:11 PM, Frank Millman  wrote:

> Hi all
>
> I know about this gotcha -
>
> x = 1.1 + 2.2
 x

>>> 3.3003
>
> According to the docs, the reason is that "numbers like 1.1 and 2.2 do not
> have exact representations in binary floating point."
>
> So when I do this -
>
> y = 3.3
 y

>>> 3.3
>
> what exactly is happening? What is 'y' at this point?
>
> Or if I do this -
>
> z = (1.1 + 2.2) * 10 / 10
 z

>>> 3.3
>
> What makes it different from the first example?
>
> Thanks
>
> Frank Millman
>
>
>
> --
> https://mail.python.org/mailman/listinfo/python-list
>
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Question about floating point

2018-08-28 Thread Chris Angelico
On Wed, Aug 29, 2018 at 12:11 AM, Frank Millman  wrote:
> Hi all
>
> I know about this gotcha -
>
 x = 1.1 + 2.2
 x
>
> 3.3003
>
> According to the docs, the reason is that "numbers like 1.1 and 2.2 do not
> have exact representations in binary floating point."
>
> So when I do this -
>
 y = 3.3
 y
>
> 3.3
>
> what exactly is happening? What is 'y' at this point?
>
> Or if I do this -
>
 z = (1.1 + 2.2) * 10 / 10
 z
>
> 3.3
>
> What makes it different from the first example?
>

Here's how you can find out *exactly* what a Python float is:

>>> y = 3.3
>>> y.as_integer_ratio()
(3715469692580659, 1125899906842624)
>>> x = 1.1 + 2.2
>>> x
3.3003
>>> x.as_integer_ratio()
(7430939385161319, 2251799813685248)
>>> z = (1.1 + 2.2) * 10 / 10
>>> z
3.3
>>> z.as_integer_ratio()
(3715469692580659, 1125899906842624)
>>> (1.1).as_integer_ratio()
(2476979795053773, 2251799813685248)
>>> (2.2).as_integer_ratio()
(2476979795053773, 1125899906842624)

The value of y is exactly (3715469692580659 / 1125899906842624) which
is 3.299822364316060. But x is a teeny bit higher.

You can explore these with the help of decimal.Decimal, too. The
values given by as_integer_ratio() are possibly more interesting in
hex than in decimal, with values like these.
-- 
https://mail.python.org/mailman/listinfo/python-list