views:

288

answers:

5

I have two floats in Python that I'd like to subtract, i.e.

v1 = float(value1)
v2 = float(value2)
diff = v1 - v2

I want "diff" to be computed up to two decimal places, that is compute it using %.2f of v1 and %.2f of v2. How can I do this? I know how to print v1 and v2 up to two decimals, but not how to do arithmetic like that.

The particular issue I am trying to avoid is this. Suppose that:

v1 = 0.982769777778
v2 = 0.985980444444
diff = v1 - v2

and then I print to file the following:

myfile.write("%.2f\t%.2f\t%.2f\n" %(v1, v2, diff))

then I will get the output: 0.98 0.99 0.00, suggesting that there's no difference between v1 and v2, even though the printed result suggests there's a 0.01 difference. How can I get around this?

thanks.

+1  A: 

You said in a comment that you don't want to use decimal, but it sounds like that's what you really should use here. Note that it isn't an "extra library", in that it is provided by default with Python since v2.4, you just need to import decimal. When you want to display the values you can use Decimal.quantize to round the numbers to 2 decimal places for display purposes, and then take the difference of the resulting decimals.

>>> v1 = 0.982769777778
>>> v2 = 0.985980444444
>>> from decimal import Decimal
>>> d1 = Decimal(str(v1)).quantize(Decimal('0.01'))
>>> d2 = Decimal(str(v2)).quantize(Decimal('0.01'))
>>> diff = d2 - d1
>>> print d1, d2, diff
0.98 0.99 0.01
Mark Byers
Could you explain how decimal could be used here to get coherent printing while still having precision arithmetic? What's wrong with the round solution?
@user248237: Added code.
Mark Byers
This is less precise than the original, simpler, more-efficient code.
Mike Graham
Check your dartboard diagrams again; I meant precise. `;)`
Mike Graham
OK... fair enough - I was wrong. There is a factor of 2 difference. But same order of magnitude... :-P
Mark Byers
A: 

The thing about the float type is that you can't really control what precision calculations are done with. The float type is letting the hardware do the calculations, and that typically does them the way it's most efficient. Because floats are (like most machine-optimized things) binary, and not decimal, it's not straightforward (or efficient) to force these calculations to be of a particular precision in decimal. For a fairly on-point explanation, see the last chapter of the Python tutorial. The best you can do with floats is round the result of calculations (preferably just when formatting them.)

For controlling the actual calculations and precision (in decimal notation, no less), you should consider using Decimals from the decimal module instead. It gives you much more control over pretty much everything, although it does so at a speed cost.

Thomas Wouters
+1  A: 

I've used poor man's fixed point in the past. Essentially, use ints, multiply all of your numbers by 100 and then divide them by 100 before you print.

There was a good post on similar issues on Slashdot recently.

Jeff
A: 

What do you mean "two significant figures"? Python float has about 15 sigfigs, but it does have representation errors and roundoff errors (as does decimal.Decimal.) http://docs.python.org/tutorial/floatingpoint.html might prove an interesting read for you, along with the great amount of resources out there about floating point numbers.

float usually has the right kind of precision for representing real numbers, such as physical measurements: weights, distances, durations, temperatures, etc. If you want to print out a certain way of displaying floats, use the string formatting as you suggest.

If you want to represent fixed-precision things exactly, you probably want to use ints. You'll have to keep track of the decimal place yourself, but this is often not too tough.

decimal.Decimal is recommended way too much. It can be tuned to specific, higher-than-float precision, but this is very seldom needed; I don't think I've ever seen a problem where this was the reason someone should use decimal.Decimal. decimal.Decimal lets you represent decimal numbers exactly and control how rounding works, which makes it suitable for representing money in some contexts.

Mike Graham
I understand the difference between the precision of calculation, and the displayed precision, i.e. the precision you'd output to a file. My example above though shows that this can lead to a misleading output. My question is: how can I get around that? What would be the decimal solution?
@user248237, If hundredths is your significant bit, than you shouldn't care that your answer is off by one hundredth. This isn't specific to floats, this is more generally a thing we deal with when representing the numbers we're dealing with approximately. "0.01" would be less true (would have a greater error) than "0.00" here, so displaying it wouldn't really be the Right Thing.
Mike Graham
When I say that we just let this happen when dealing with displaying numbers inexactly, I don't mean that we do this when we are sloppy or lazy; for example, enter your test numbers in Excel or OOCalc and turn the display to two digits after the decimal place—this same little hiccup will happen.
Mike Graham
A: 

You could format it using a different format string. Try '%.2g' or '%.2e'. Any decent C/C++ reference will describe the different specifiers. %.2e formats the value to three significant digits in exponential notation - the 2 means two digits following the decimal point and one digit preceding it. %.2g will result in either %.2f or %.2e depending on which will yield two significant digits in the minimal amount of space.

>>> v1 = 0.982769777778
>>> v2 = 0.985980444444
>>> print '%.2f' %v1
0.98
>>> print '%.2g' %v1
0.98
>>> print '%.2e' %v1
9.83e-01
>>> print '%.2g' %(v2-v1)
0.0032
>>> print '%.2e' %(v2-v1)
3.21e-03
D.Shawley
@D.Shawley: I think that the OP wanted the result with 2 decimal places, not two significant figures. I've updated the question to what I think was meant.
Mark Byers