benlime

benlime

Hey guys,

I have a short and simple question, which I am currently unable to figure out using the documentation of the Jason library.

I have a struct which contains some keys with values of the Decimal struct. I would like to have the Decimal struct (I am using the Decimal Library for it) always converted to float when encoding to JSON. So far I only know that I would have to implement the Json.Encoder protocol and have the encode method to call Decimal.to_float(value). But I am not sure how to do that? How do I implement the protocol?

I have tried to just make a file like this:

defimpl Jason.Encoder, for: Decimal do
  def encode(struct, opts) do
    Jason.Encode.map(Decimal.to_float(struct), opts)
  end
end

However, this code never runs. Can anybod help? :slight_smile:

Thanks guys.

Showing Posts 1 to 10

NobbZ

NobbZ

Please do not use floats. You will use all the precision you initially got by using decimal. Please do either use a String or dump into a JSON object.

Aside of that, you should use Jason.Encode.float/1. From what I can see in the snippet you posted, the code should crash rather than not being run. Can you therefore reproduce the problem in a fresh project and put it on github?

benlime

benlime OP

Thanks for your reply. Can you describe why using a floating point number in JSON is a bad idea? I am using Decimal internally to handle money values and want to return those to the frontend just for displaying in the view. Having the number as a string representation feels wrong to me. However, the default encoding encodes the value to a string. I am not sure where this logic comes from.

edit: Okay I found a pretty good explanation of why I should not use floating points on StackOverflow. I get it - but just for my personal knowledge, the question still remains - even I would not use it anymore.

I don’t think a demo project is needed. I just can’t figure out how to make the code run. I only created a file decimal_encoder.ex with the protocol implementation. But I need to use or register the implementation anywhere so that Jason knows it should use my decoder, don’t I? And this is where I am stuck.

NobbZ

NobbZ

Floating point numbers might have their right to exist in JSON, but definitively not when your datasource is a Decimal, as Decimal allows for very exact values, which float doesn’t.

This is good.

But to be honest, in JSON the only way to represent numbers in a safe way, as JSON does not differentiate between integers and floats, but knows only about numbers. So numbers that look integral might get coerced to float without you beeing able to control.

From jason:

https://github.com/michalmuskala/jason/blob/master/lib/encoder.ex#L204-L210

So you can not change it.

And here we already have a problem. What is encoded as 1 Euro and 5 cent might get rendered as 1.05 in the JSON, but as this probably not an exact value in Float this might get loaded as 1.04999999 (or similar) and displayed as such. Or even worse, the client truncates or rounds, or does other things out of your control.

Your user has the right to get the same (or at least equivalent) information that you are working with, especially when its the users money!

benlime

benlime OP

Thank you so much for your explanation. This totally makes sense and I’ll stick with strings for representing money values.

I was looking at the Jason Source as I was expecting something lime this, but I must have missed it. Thanks for sharing this. :blush:

NobbZ

NobbZ

I knew where to look :wink: I already assumed Jason where implementing for Decimal when I’ve seen that decimal is an optional dependency of it.

Also defimpls are usually in the same file as the struct or the protocol, and as Jason does only define the protocol it was clear where to look, as we can safely assume that @michalmuskala organizes his code idiomatically.

OvermindDL1

OvermindDL1

Ack!!!

Among others!

Never ever ever ever ever use floats in relation to money, ever ever ever!

/me hopes they were clear enough ^.^;

benlime

benlime OP

I think you missed one „ever“. (I got it, I got it. :stuck_out_tongue:)

zenw0lf

zenw0lf

Hmm, I thought that the real problem with floats was trying to operate with them, not displaying them per se.

I know the famous: 0.1 + 0.2 # == 0.30000000000000004, but if you are operating on Decimals in Elixir and do something like:

Decimal.add(Decimal.new("0.1"), Decimal.new("0.2"))

And then convert the result to a float, it’ll be present just right, as 0.3.

What I’m trying to get at, in the end I always have to use parseInt, parseFloat or Number on the frontend to convert those decimal strings into numbers, because you usually need those values for validations, limits, steps, etc.

Honest question:
Is there really a problem when going from a string representation to a float?

Is there an example of a decimal string representation that when converted to a float loses precision? Something like having "1.05" => converts to float => 1.049999?

NobbZ

NobbZ

There are enough, just take 0.3, you can not represent that value as float32, the same is probably true for float64, but you can at least have a closer approximation.

Play around with the float converter, it shows you what is possible and what is not.

Also, just as an exercise. Try to find n and m: 0.3 = m / (2 ** n)

Even though that is not the real representation, it will give you an estimate what the problem is.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Sure, here’s a concrete example:

Suppose you have a service where people want to send you numbers, and you’re going to do some kind of processing with those numbers, and then send back a result. Suppose it’s super simple, like a math test for kids where they’re asked to round a number.

The task is: Round “2.675” to the nearest hundredths place.

You take that value, and parse it to a float:

iex(7)> {value, _} = Float.parse("2.675")
{2.675, ""}

Yay! that looks right!

iex(8)> value |> Float.round(2)          
2.67

Oh no…

This happens because it isn’t actually 2.675, it’s 2.67499999999999982236431605997495353221893310546875 and Elixir is nice and displays something more friendly.

Yes, if you care about precision at all. To be clear, plenty of times you don’t care about precision. Maybe you’re doing geolocation stuff where precision is carried as a separate term anyway, maybe you’re doing something in a GPU with floating point math. But if people want you to treat the number as it is written, you can’t convert it to a float, at all.

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
psy-q
I’m trying to set up Emacs with elixir-ls via lsp-mode and credo via Flycheck. This should mostly be preconfigured as Flycheck picks up c...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews