# Fixed point decimals for accounting

What are some efficient ways to implement fixed point decimals, for tasks like accounting?

https://juliahub.com/ui/Search?q=decimal&type=packages

Both Decimals.jl and DecFP.jl look quite good.

Alternatively, you could just do all computations in integer cents and divide by 100 for display.

3 Likes

ok thanks, I found fixedpointdecimal.jl looked really good. But, the output is not just a number, but also a description.

I thought of dividing integers, but that means inputting values multiplied by 100, and also could create problems when weighting.

I wasnâ€™t sure from the documentation for Decimals.jl or DecFP.jl, to actually fix the decimals, when numbers are manipulate.

Thatâ€™s because they are floating-point decimal types. Why do you need fixed point specifically, as opposed to decimal floating-point?

2 Likes

Yes, exactly.

2 Likes
``````struct AccountingNumber <: Real
x::Int

AccountingNumber(x::Number) = new(round(Int, 100*x))
end

Base.show(io::IO, x::AccountingNumber) = Printf.@printf(io, "%.2f", x.x/100)
...
``````

Add some conversion and promotion methods and you should be home.

3 Likes

But what if I have a number like 1.999999, it would round to 2.00 and not 1.99

Correctly, I would have thought. Is the accounting standard to round down? In that case, replace `round` in the constructor by `floor`.

OK, that would work. Usually, there is only 2 decimals, there are cases, of 1/10th cent, there normal rules apply. The problem is that Iâ€™m not rounding, and the values need to balance exactly.

1 Like

You could have

``````struct AccountingNumber
x::Int
nofdecimals::Int

AccountingNumber(x, nofdecimals=2) = new(floor(Int, x.x*exp10(nofdecimals)), nofdecimals)
end
``````

Might be a bit overkill for those mills though.

I think I have an idea of things to play with. How do I call a struct?

If it has the constructor i suggested above, then `n = AccountingNumber(6.24)` will create an `AccountingNumber` representing `6.24` (by way of the fields `x=624; nofdecimals=2`). Is that what you mean by â€ścall a structâ€ť?

Yes, I thing it is. But;

``````struct AccountingNumber
x::Int
nofdecimals::Int

AccountingNumber(x, nofdecimals=2) = new(floor(Int, x.x*exp10(nofdecimals)), 		nofdecimals)
end

n=AccountingNumber(6.25)
``````

gives an error;

``````Type Float64 has no field x
``````

Why is there no field?

My bad, the constructor should have `x`, not `x.x`. As an argument to the constructor itâ€™s a normal number, so it doesnâ€™t have any fields.

1 Like

It seems like you are re-inventing the wheel hereâ€¦

(I really donâ€™t understand why you wouldnâ€™t just use decimal floating point.)

1 Like

If your goal is to use the numbers for accounting I agree. But if you want to figure out how to implement the numbers (which is how I read the question), thatâ€™s its own challenge and this is how Iâ€™d start solving it.

It might make sense to make a specific type for this (even with decimal numbers), since apparently the rounding rules in accounting are funky.

There are multiple fixed-point and decimal-float packages; why not look at them?

It might make sense to make a specific type for this (even with decimal numbers), since apparently the rounding rules in accounting are funky.

Decimal floating-point supports multiple rounding modes already, and will typically accumulate errors (in a long sequence of calculations) much more slowly than fixed point.

But the OP hasnâ€™t clearly defined their application or their requirements, so we are just guessing here. Thatâ€™s part of why I keep asking to find out what specifically is wrong (if anything) with decimal floating point for their application.

3 Likes

Iâ€™ve looked at several, and this is much the same. Iâ€™m thinking floor would be useful here, if each term had 3 or more decimals. Iâ€™ll see if there is a way to concact 0. It might not be an issue to just use floats, but the problem, is that Iâ€™m worried that some of the tests wonâ€™t work right, if the difference between certain values is not exactly 0. I could make a MWE to illustrate this better.