views:

356

answers:

4

Hello,

I have a simple string that I want to read into a float without losing any visible information as illustrated below:

s = '      1.0000\n'

When I do f = float(s), I get f=1.0

How to trick this to get f=1.0000 ?

Thank you

+7  A: 

Direct answer: You can't. Floats are imprecise, by design. While python's floats have more than enough precision to represent 1.0000, they will never represent a "1-point-zero-zero-zero-zero". Chances are, this is as good as you need. You can always use string formatting, if you need to display four decimal digits.

print '%.3f' % float(1.0000)

Indirect answer: Use the decimal module.

from decimal import Decimal
d = Decimal('1.0000')

The decimal package is designed to handle all these issues with arbitrary precision. A decimal "1.0000" is exactly 1.0000, no more, no less. Note, however, that complications with rounding means you can't convert from a float directly to a Decimal; you have to pass a string (or an integer) to the constructor.

Chris B.
Very descriptive :)
Gökhan Sever
The precision or imprecision of floats has nothing to do with it. *Floating-point* representations (by definition) are implemented in essentially the equivalent of scientific notation: with a mantissa and exponent. Since every number is stored as 0.XXXXX times 2 to the YYYY power, decimal places on the right side of the decimal point are meaningless. Base-10 floating point systems (like the Python `Decimal` class) use an internal variable to keep track of how many digits of precision are desired, and reproduce that many digits on output, but otherwise they would be in exactly the same boat.
Daniel Pryden
I wasn't using "precision" in the strictest sense, but in the "significant digits to the right of the decimal" sense. And while the Python `Decimal` class *may* be a decimal floating point system, the fact is it does support "exact unrounded decimal arithmetic". And it seems that an internal variable to track the precision is *exactly* what the original poster was looking for.
Chris B.
+5  A: 
>>> 1.0 == 1.00 == 1.000
True

In other words, you're losing no info -- Python considers trailing zeros in floats' decimal parts to be irrelevant. If you need to keep track very specifically of "number of significant digits", that's also feasible, but you'll need to explain to us all exactly what you're trying to accomplish beyond Python's normal float (e.g., would decimal in the Python standard library have anything to do with it? etc, etc...).

Alex Martelli
I know they are equal that is why I explicitly used the word "visible". I just want to preserve the zeros to comply with my special header standards. If I don't do these in the reading step, I will have to while re-creating the same header using this information. There it is easy, just to use formatting operators for print. Thanks for the explanation.
Gökhan Sever
@gsever: If you're trying to round-trip a number with exact formatting, perhaps it would be easiest just to store both the input string and the floating point representation? However, if you're not doing any fancy math on the value, I think `Decimal` probably is the answer you want.
Daniel Pryden
No, 1.0 != 1.00 at least in a scientific term. 1.00 has a precision of +-0.01, while 1.0 has a precision of +-0.1
dalloliogm
@dalloliogm: it's a question of presentation (read *string formatting*), not of nulls' importance.
SilentGhost
@dalloliogm, the mathematical theory of real numbers (underlying anything from high-school algebra to calculus etc) does not have the concept of "precision" (or "significant digits" or "tolerance" any equivalent simplification of interval-arithmetic). No doubt in science and engineering we do need to augment measurements with error bounds, fraught as it may be with difficulties, but, as a notation for that, declaring the number of trailing zeros to be significant is no help. If a=1.0 and b=1.00, what's a+b? a-b? a*b? a/b? If we can't even do THIS, what good is it at all?!
Alex Martelli
+1  A: 

Hi gsever,

Indulge me while I reinvent the wheel a little bit! ;)

Should you want the reprsentation of the object (the way it is displayed in the shell) to have the form 1.0000 then you may need to change the __repr__ method of the float. If you would like the object to be printed to a file or screen you will need to change the __str__ method. Here is a class that will do both.

class ExactFloat(float):
    def __repr__(self):
        return '%.4f' % self
    __str__ = __repr__


# usage of the shiny new class to follow..
>>> f = ExactFloat('    1.0000\n')
>>> f
1.0000
>>> print f
1.0000

Now there are also a myriad of other better ways to do this, but like I said, I love re-inventing wheels :)

Simon Edwards
I share your sentiments on your reinvention on the wheel since I have done the same thing today. See http://stackoverflow.com/questions/1449139/simple-object-recognition. To me a very elegant solution, however we might need to add some spices to make it more generic. Probably I will forget about the 0's and later use @Chris B. suggested.
Gökhan Sever
+1  A: 

Here's an even shinier version of @Simon Edwards' ExactFloat, that counts the number of digits after the period and displays that number of digits when the number is converted to a string.

import re

class ExactFloat(float):
    def __init__(self, str_value):
        float.__init__(self, str_value)
        mo = re.match("^\d+\.(\d+)", str_value)
        self.digits_after_period = len(mo.group(1))

    def __repr__(self):
        return '%.*f' % (self.digits_after_period, self)

    def __str__(self):
        return self.__repr__()


print ExactFloat("1.000")
print ExactFloat("1.0")
print ExactFloat("23.234500")

Example:

$ python exactfloat.py
1.000
1.0
23.234500
codeape
This is shining indeed. Thank you for your time for creating this nice example.
Gökhan Sever