Changing runtime precision. A question about FixedPointDecimals

I’m working on economic simulations and have used the following shortcut in my code:

Currency = Fixed(4)

Now I’ve run into a situation where I would like to change the precision in some cases. Redefining Currency before running the code has no effect. I guess the pre-compiler already replaced all occurrences of Currency with Fixed(4).

Is there a way that I can redefine Currency somehow before running my simulation code so that is has the desired effect? I guess I would have to define Currency in another way. If the possibility exists, what would the performance cost be?

Thanks in advance!

Could you share a few more details of the problem?

  • Are you using FixedPointDecimals? If so, do you mean this?
    Currency = FixedDecimal{Int, 4}
    
    At least in the latest version of FixedPointDecimals (v0.4.3) it says the binding Fixed doesn’t exist.
  • Are you defining the type alias in a module? Then defining Currency outside of that module scope would have no effect (and redefining a variable in another module is not allowed).

At least in the REPL, changing the value of Currency works for me:

julia> using FixedPointDecimals

julia> Currency = FixedDecimal{Int, 4}
FixedDecimal{Int64, 4}

julia> function return_currency()
           x = rand()
           return Currency(x)
       end
return_currency (generic function with 1 method)

julia> return_currency()
FixedDecimal{Int64,4}(0.3279)

julia> Currency = FixedDecimal{Int, 5}
FixedDecimal{Int64, 5}

julia> return_currency()
FixedDecimal{Int64,5}(0.55454)

If it is defined in a module, adding a “setter” method might work:

julia> module Simulation

       using FixedPointDecimals

       Currency = FixedDecimal{Int, 4}

       function run_simulation()
           x = rand()
           return Currency(x)
       end

       function set_currency_precision(newPrecision)
           global Currency
           Currency = FixedDecimal{Int, newPrecision}
       end

       end
Main.Simulation

julia> Simulation.run_simulation()
FixedDecimal{Int64,4}(0.6850)

julia> Simulation.set_currency_precision(5)
FixedDecimal{Int64, 5}

julia> Simulation.run_simulation()
FixedDecimal{Int64,5}(0.13342)

I forgot to mention I also defined:

Fixed(digits::Integer) = FixedDecimal{Int128, digits}

Currency is indeed defined in a module. I tried the fix you mentioned but it didn’t work. I use Currency as a field type in some structs in the module.

# Simplified example

struct Balance
  balance::Currency
end

Setting the precision to a different number results in an error when those structs are used:

InexactError: convert(FixedPointDecimals.FixedDecimal{Int128, 4}, -170141183460469231731687303715884.105728)

I’m guessing that the following would fix the problem:

mutable struct Balance
  value::FixedDecimal
  Balance(value::Real) = new(Currency(value))
end

function set_balance(balance::Balance, value::Real)
  balance.value = Currency(value)
end

What would the performance hit be on a solution like that? Applying it would mean quite some work on my code base since I’m using Currency all over the place.

Yes, if you set the field type of Balance to be Currency, it will be hard-coded to be whatever precision you had at that time. Changing field types of structs after their definition is not possible as far as I know.

I’d say it works, but using non-concrete field types usually leads to worse performance. See here for example.

It sounds like a cleaner solution might be to use a type parameter. If you don’t change the precision very often, I don’t think the performance would suffer (if you use a lot of different currency types, the cost of recompilation might go up – but when in doubt, benchmark).

struct Balance{C<:FixedDecimal}
  balance::C
end

That makes the whole type carry the type C (currency type) with it. If the constructor is not too complicated, it should also work to infer the type without changing the code.

E.g. if you constructed a Balance object before with b = Balance(Currency(0.50)) then this line should still work after you redefine the module variable Currency with some setter function.

2 Likes

Yes! That would do the trick! There would only be a performance hit when the simulation is initialised. That’s totally acceptable.

Thanks!

2 Likes