2
votes

I don't know if it's an Ecto's or Elixir's issue. When I have a number in database like 1000, 15000 etc. Ecto returns it as 1.0e3, 1.5e4... If I change that 1000 to 1234 everything is ok.

Elixir 1.3.2, Ecto 2.0.3, MySQL

Migration:

alter table(:incoming_transfers) do
  add :fee, :float # Ecto translates it as DOUBLE in MySQL
end

Schema:

schema "incoming_transfers" do
  field :fee, :float
end

If I change manually in my database type DOUBLE to FLOAT the problem remains. But if I change it to INTEGER, it's good.

2
What type did you use for that column in the migration for the table and the schema declaration in the model? Can you please include the relevant snippets of both of those files in the question? - Dogbert
@Dogbert I have updated my question - Jakub Zawiślak
So - maybe it's a stupid question- why you want to keep integer numbers as floats? Just use integer in schema. - PatNowak
@PatNowak this was just an example. When I had this issue for the first time, I fixed this by converting Float to Money, but now I must to use Float. - Jakub Zawiślak

2 Answers

2
votes

Elixir's Inspect implementation for Float just delegates to :io_lib_format.fwrite_g/1 and :io_lib_format.fwrite_g/1's source in Erlang/OTP mentions that it tries to print the number accurately in the least number of characters possible. So, 1000 is converted to 1.0e3 because that's shorter than 1000.0, but 100.0 is not converted to 1.0e2 because that's not shorter than 100.0. Similarly 12300.0 is converted to 1.23e4 because that's shorter.

%%  Writes the shortest, correctly rounded string that converts
%%  to Float when read back with list_to_float/1.
%%
%%  See also "Printing Floating-Point Numbers Quickly and Accurately"
%%  in Proceedings of the SIGPLAN '96 Conference on Programming
%%  Language Design and Implementation.

Erlang:

1> [1000.0, 100.0, 12300.0].
[1.0e3,100.0,1.23e4]

Note that this does not result in losing any accuracy (beyond what floating numbers already lose). 1000.0 is exactly equal to 1.0e3.

2
votes

For those who want to override function mentioned by @Dogbert. Just put it in your code:

defimpl Inspect, for: Float do
  def inspect(term, _opts) do
    IO.iodata_to_binary(:io_lib_format.fwrite_g(term))
  end
end

and change :io_lib_format.fwrite_g(term) to something like :erlang. float_to_binary(term, [decimals: 6]). For example, for 7.12 it returns "7.120000".

Anyway, I think it would be better to use Decimal instead of Float.