Elixir uses scientific notation by default for floats greater then 1000. This causes an undesired side effect during json serialization.
iex(5)> val = 1000.00
1.0e3
iex(11)> Poison.encode(val)
{:ok, "1.0e3"}
The output I would like is
iex(11)> Poison.encode(val)
{:ok, "1000.00"}
I have seen this answer that uses :erlang.float_to_binary(0.005 * 2.7 / 100, [:compact, {:decimals, 10}])
or :io.format("~f~n",[0.005 * 2.7 / 100])
, but this would require to patch Poison
or to manually check all data before encoding.
Is there a cleaner way to force the default flot to binary format in elixir ?
Poison
allows to implement Poison.Encoder
protocol. The implementation for Float
obviously exists, that’s why I’d suggest to wrap your floats into custom FloatStruct
upfront:
defmodule FloatStruct do
defstruct value: 0.0, format: [:compact, {:decimals, 10}]
end
defimpl Poison.Encoder, for: FloatStruct do
def encode(%{value: value, format: format}, options) do
Poison.Encoder.BitString.encode(
:erlang.float_to_binary(value, format), options)
end
end
I understand that this would require traversing nested terms to wrap Float
s with FloatStruct
s, but I don’t see any easier approach. I might be wrong, though.
One might support both mentioned in the OP formats for free:
defimpl Poison.Encoder, for: FloatStruct do
def encode(%{value: value, format: format}, options) do
Poison.Encoder.BitString.encode(
format(value, format), options)
end
defp format(value, format) when is_list(format),
do: :erlang.float_to_binary(value, format)
defp format(value, format) when is_binary(format),
do: :io.format(format, [value])
end