Question

I have a python function that checks if the value passed is a valid number. The values passed are in string format.

def is_valid_numeral(text_to_check):
    '''Check if passed value is a valid numeral figure'''
    try: 
        float(text_to_check)
        return True
    except ValueError:
        return False

A value like 6E+007 is passed and this function returns True which is correct.

After the above function, I need to save the passed figure into a Decimal field. So I run a format_decimal function as below

def format_decimal(value, decimals): 
    '''Format a value to "n" decimal places
    http://mkaz.com/solog/python/python-string-format.html
    '''
    try:
        if decimals == 2:
            value =  int(value)/100
            return "{:.2f}".format(int(value))
    except ValueError:
        raise 

At this point, it fails with the error:

`Value Error: Invalid literal for int() with base 10:6E+007`

Since this really is a valid figure just in a different notation, is there a function that can convert this type of value for me and then pass it through the format_decimal function?

Was it helpful?

Solution

You're checking the validity by converting to float, but actually doing the conversion with int - the discrepancy is causing your problems.

The decimal module is the best way to convert large format numbers.

>>> int(decimal.Decimal('6E+007'))
60000000

You don't want to do a double conversion to float then int because that can result in rounding errors.

>>> int(float('1e23'))
99999999999999991611392L
>>> int(decimal.Decimal('1e23'))
100000000000000000000000L

OTHER TIPS

Hmm. You changed the original question; the way it is now it should be trivial: since you're checking for whether float() works now, just be consistent with that: stop using int()! That is, change to this:

def format_decimal(value, decimals):
    try:
        if decimals == 2:
            value = float(value)/100
            return "{:.2f}".format(value)
    except ValueError:
        raise

But the code is still confused:

  1. Why are you dividing by 100? Do you really want, e.g., "1234" to change into "12.34"?
  2. What if decimals isn't 2? Then your format_decimal() does nothing: it "falls off the end" and so returns None.
  3. Catching an exception and immediately re-raising it is the same as not bothering to catch it to begin with. That is, the try/except structure, as written, does nothing except complicate the code.

It would really help if you gave exact examples of input strings and their expected output strings.

Generalizing

Try this?

def format_decimal(value, decimals=2):
    from decimal import Decimal
    # divide value by 10**decimals; this is just scaling
    value = Decimal(value).scaleb(-decimals)
    return "{:.{d}f}".format(value, d=decimals)

That allows specifying the scaling factor (decimals). Here's sample input/output. If anything isn't what you want, say exactly what it is that you do want:

for v in "1", "1234.5678", "1.2e6":
    for d in range(4):
        print repr(v), d, "->", repr(format_decimal(v, d))

and the output:

'1' 0 -> '1'
'1' 1 -> '0.1'
'1' 2 -> '0.01'
'1' 3 -> '0.001'
'1234.5678' 0 -> '1235'
'1234.5678' 1 -> '123.5'
'1234.5678' 2 -> '12.35'
'1234.5678' 3 -> '1.235'
'1.2e6' 0 -> '1200000'
'1.2e6' 1 -> '120000.0'
'1.2e6' 2 -> '12000.00'
'1.2e6' 3 -> '1200.000'

The problem is simple. 6E+007 is not an integer notation, but a float notation.

Instead of int('6E+007'), use float first:

int(float('6E+007'))

Or, use the decimal module:

import decimal
int(decimal.Decimal('6E+007'))

Also, IMO if you want the number to have two decimal places, you should use float instead, since the value of an integer cannot contain any fractional component, and will always be like that. For example, int('1.5') will always be 1, not 1.5.

So just use:

value = float(value/100) #Well, i'll keep the division here,
# since i don't know what it's about.
return ":{.2f}".format(value)

Hope this helps!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top