我正在使用 Elixir 的 Decimal 模块:
https://hexdocs.pm/decimal/readme.html
我编写了自己的宏来更新精度:
defmodule Mars.Decimal do
defmacro __using__(_) do
quote do
require Decimal
%{Decimal.Context.get() | precision: 2}
|> Decimal.Context.set()
end
end
问题:
这似乎根本不影响精度。如何全局设置精度?
目前,Decimal 并不等同于混合精度,即
Decimal.new(1) != Decimal.new("1.0")
。
我希望如果我可以将精度设置为在任何地方都固定,我可以解决这个问题。
作为一个补充问题;我正在尝试对货币执行此操作,因此它始终是小数点后两位。 Decimal 模块的精度仅设置总精度(即最多 2 位数字,无论
.
的哪一侧,即 3456.123 -> 3500)。如果 Elixir 中有更干净的处理货币数字的方法,请告诉我!
根据这篇文章https://elixirforum.com/t/how-to-set-default-decimal- precision/40404,需要设置精度在您希望其生效的每个进程中 。例如,将
Decimal.Context.set(%Decimal.Context{Decimal.Context.get() | precision: 2})
粘贴到 iex
会话中,does 会影响 iex
中发生的所有后续计算,因为 iex
在其自己的进程中运行。
但是,这对回到“现实世界”没有帮助,在“现实世界”中,您的应用程序可能会在许多不同(且未知)的进程中执行。我认为为您的特定用例充实几个示例可以使文档受益。
考虑到
Decimal
将其设置存储在进程字典中的方式,您需要将所有计算隔离到单个进程中,例如在GenServer
。然后,您与它的交互将调用该特定进程,其中上下文已设置为精确到小数点后两位。
这里是一个示例
GenServer
,将小数精度设置为 2:
defmodule Foo.Precision2 do
use GenServer
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
def init(state) do
Decimal.Context.set(%Decimal.Context{Decimal.Context.get() | precision: 2})
{:ok, state}
end
def handle_call({:div, x, y}, _from, state) do
result = Decimal.div(x, y)
{:reply, result, state}
end
def div(x, y) do
GenServer.call(__MODULE__, {:div, x, y})
end
end
然后您需要使用您的应用程序启动它,例如里面
application.ex
:
defmodule Foo.Application do
@moduledoc false
use Application
@impl true
def start(_type, _args) do=
opts = [strategy: :one_for_one, name: Foo.Supervisor]
Supervisor.start_link(
[
{Foo.Precision2, []}
],
opts
)
end
end
最后,您可以将调用路由到
GenServer
的客户端函数,例如
iex> Foo.Precision2.div(100,3)
#Decimal<33>
诚然,这感觉像是一个荒谬的设置量来支持一个相当直接的用例(并且肯定有更干净的方法来实现这一点),但它似乎有效。这种方法的一个优点可能是您可以维护多个上下文,并为每个上下文设置特定的设置。