# Safely convert dollars to cents

For my project, I need to convert dollar to cents, so I wrote the following function to safely do the conversion. The input dollar is float and the output should be int.

def safe_dollar_to_cent(dollar):
parts = str(dollar).split('.')

msg = 'success'
status = 0
if len(parts) == 1:
return status, int(parts[0])*100, msg

decimal_part = parts[1]
if len(decimal_part) > 2:
decimal_part = decimal_part[0:2]
msg = 'dollar has been truncated: {} -> {}'.\
format(parts[1], decimal_part)
status = 1

ret = int(parts[0]) * 100
multiplier = 10
for i, s in enumerate(decimal_part):
ret += int(s) * multiplier
multiplier /= 10

return status, ret, msg


I am posting here for seeking other some pythonic way of doing this job.

Update:

my input is expected to be float, the return value should be int. The reason of this implementation is that I found the following incorrect computation.

 18.90 * 100 = 1889.9999999999998 

-
What are you expecting dollar to be, a number or a string? – SuperBiasedMan Feb 25 at 11:56
Just to understand what seems overly complicated, what was wrong with return int(dollar * 100)? – Mathias Ettinger Feb 25 at 11:58
You aren't using fixed-point dollar values? Shame on whoever provided you that interface, floating-point math is anathema for financial stuff. docs.python.org/2/library/decimal.html – JAB Feb 25 at 15:47
If you are in total control of your project, consider using cents instead of dollars. Otherwise, consider using Decimal numbers and you w ill have no issue by directly doing int(d * 100.), ((d * 100.) - int(d * 100.)) / 100. to return a pair having the result and the data you lost – Luis Masuelli Feb 25 at 17:53
About your update: you should not use floating point to store money values: what you type as 18.90 is really stored as a binary fractional number, with a little error. But anyway if you use doubles (8 bytes, precision 52 bits), all you need is rounding after the multiply. The error will disappear – edc65 Feb 25 at 20:25

I don't like the 3 argument return you're doing. You're using status and msg to actually do the same thing. They both return a signal of success unless the dollar is truncated. You don't need both pieces of information. Personally, I'd just say that you only need to print a note about the dollar being truncated so that the function only returns the cents value.

Now, you're also overlooking a very simple formula to convert this:

cents = dollars * 100


You don't need anything more complicated than that for the basic function:

def safe_dollar_to_cent(dollar):
cents = dollar * 100
return cents


If you're not sure that dollar will be a number, you can try converting it:

def safe_dollar_to_cent(dollar):
cents = float(dollar) * 100
return cents


As for truncating, I think it's better as an option. Let the user choose whether or not something will be truncated:

def safe_dollar_to_cent(dollar, truncate=True):
cents = float(dollar) * 100
if truncate:
return int(cents)
else:
return cents


int will turn cents into a whole number instead of a decimal, so it's effectively truncating everything after the ..

-
I think the statements in the if truncate are swapped. Also, instead of printing a note, maybe use warnings.warn? – Sjoerd Job Postmus Feb 25 at 15:15
@SjoerdJobPostmus You are correct! Thanks for catching that. You could post a separate answer about warnings.warn. – SuperBiasedMan Feb 25 at 15:40
18.90 * 100 = 1889.9999999999998 in my python 2.7.5. – Alex Feb 25 at 17:49

First of all, good job on the solution. It works nicely. Contrary to the other answer here, returning boolean success and message is a industry convention.

What I would suggest is to split the validation and conversion into two functions like this:

def validate_dollar(dollar):
return dollar * 100 % 1 == 0

def safe_dollar_to_cent(dollar):
if validate_dollar(dollar):
return {'success': True, 'cents': int(dollar*100)}
else:
return {'success': False, 'msg': 'Malformed input'}

print(safe_dollar_to_cent(10.9))
print(safe_dollar_to_cent(10))
print(safe_dollar_to_cent(10.90))
print(safe_dollar_to_cent(10.999))

-