My application requires a fractional quantity multiplied by a monetary value.
For example, $65.50 × 0.55 hours = $36.025
(rounded to $36.03
).
I know that floats should not be used to represent money, so I'm storing all of my monetary values as cents. $65.50
in the above equation is stored as 6550
(integer).
For the fractional coefficient, my issue is that 0.55
does not have a 32-bit float representation. In the use case above, 0.55 hours == 33 minutes
, so 0.55
is an example of a specific value that my application will need to account for exactly. The floating point representation of 0.550000012
is insufficient, because the user will not understand where the additional 0.000000012
came from. I cannot simply call a rounding function on 0.550000012
because it will round to the whole number.
Multiplication solution
To solve this, my first idea was to store all quantities as integers and multiply × 1000. So 0.55
entered by the user would become 550
(integer) when stored. All calculations would happen without floats, and then simply divide by 1000 (integer division, not float) when presenting the result to the user.
I realize that this would permanently limit me to 3 decimal places of precision. If I decide that 3 is adequate for the lifetime of my application, does this approach make sense?
Are there potential rounding issues if I were to use integer division?
Is there a name for this process? EDIT: As indicated by @SergGr, this is fixed-point arithmetic.
Is there a better approach?
EDIT:
I should have clarified, this is not time-specific. It is for generic quantities like 1.256 pounds of flour
, 1 sofa
, or 0.25 hours
(think invoices).
What I'm trying to replicate here is a more exact version of Postgres's extra_float_digits = 0
functionality, where if the user enters 0.55
(float32), the database stores 0.550000012
but when queried for the result returns 0.55
which appears to be exactly what the user typed.
I am willing to limit this application's precision to 3 decimal places (it's business, not scientific), so that's what made me consider the × 1000
approach.
I'm using the Go programming language, but I'm interested in generic cross-language solutions.