0

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 ?

noscreenname
  • 3,314
  • 22
  • 30

1 Answers1

2

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 Floats with FloatStructs, 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
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160