benlime

benlime

How to encode Decimal with Jason Library to float?

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.

Most Liked

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:

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!

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.

https://www.h-schmidt.net/FloatConverter/IEEE754.html

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.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Yeah if people care about precision, they should avoid floats at all layers and use decimal libraries at all layers.

wojtekmach

wojtekmach

Hex Core Team

Thanks for writing down what worked for you, always appreciated!

My only suggestion would be to add the following check to catch values that’d be considered invalid:

+ if Decimal.inf?(value) or Decimal.nan?(value) do
+   raise "cannot encode #{inspect(value)} to JSON"
+ end

  Decimal.to_string(value, :normal)

Where Next?

Popular in Questions Top

JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
Kagamiiiii
Student & New to elixir. Nice language. I want to convert a english character, e.g. “a”, which is stored in a variable, to it’s asci...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
kostonstyle
Hi all I want to have a unix time, from the current time plus 1 hour. DateTime.now + 1 hour How to get it in elixir? Thanks
New
chewm
Hi guys, nice to meet you to the whole forum, I’m new here, I’m trying to configure visual studio code for elixir, right now the intellis...
New
wernerlaude
In AR this is so simple @articles = current_user.articles How to do in Ecto? def index(conn, _params) do current_user = conn.assig...
New

Other popular topics Top

lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
273 38985 115
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New

We're in Beta

About us Mission Statement