kip

kip

ex_cldr Core Team

Decimal.to_integer strangeness

I’m hoping smarter eyes than mine can help me resolve this issue. I am attempting to convert a decimal to an integer. The Decimal implementation uses guard clauses to decide if such as conversion is possible (good!). But for some reason I’m seeing guard clauses fail even when the data looks like it should pass. Here’s the example:

# A decimal. Shouldn't be able to be converted to an integer
# but it looks like the guards for the third clause should match and I
# don't know why they aren't!
iex> d = Decimal.new("1234.50")
#Decimal<1234.50>

# Here's what the struct underneath looks like:
iex> inspect d, structs: false
"%{__struct__: Decimal, coef: 123450, exp: -2, sign: 1}"

# Lets check the remainder of the coef
iex> Kernel.rem(d.coef,10)
0

# Error raises on conversion suggesting no guards match but
# I think the third clause should match?
iex> Decimal.to_integer d                     
** (FunctionClauseError) no function clause matching in Decimal.to_integer/1    
    
    The following arguments were given to Decimal.to_integer/1:
    
        # 1
        #Decimal<1234.5>
    
    Attempted function clauses (showing 3 out of 3):
    
        def to_integer(%Decimal{sign: sign, coef: coef, exp: 0}) when is_integer(coef)
        def to_integer(%Decimal{sign: sign, coef: coef, exp: exp}) when is_integer(coef) and exp > 0
        def to_integer(%Decimal{sign: sign, coef: coef, exp: exp}) when is_integer(coef) and exp < 0 and rem(coef, 10) == 0
    
    (decimal 2.0.0) lib/decimal.ex:1322: Decimal.to_integer/1

But in my example is_integer(coef) == true and exp < 0 == true and rem(coef, 10) == 0 so why isn’t this clause being executed?

Probably an issue with me staring at a screen for too long so any guidance appreciated.

Marked As Solved

Aetherus

Aetherus

I did my experiment by copy and paste:

defmodule MyDecimal do
  # `to_integer/1` is copied from the source code of `Decimal`,
  # and added some `IO.inspect`

  def to_integer(%Decimal{sign: sign, coef: coef, exp: 0} = d)
      when is_integer(coef) do
        IO.inspect(d, label: "Branch 1", structs: false)
        sign * coef
      end

  def to_integer(%Decimal{sign: sign, coef: coef, exp: exp} = d)
      when is_integer(coef) and exp > 0 do
        IO.inspect(d, label: "Branch 2", structs: false)
        to_integer(%Decimal{sign: sign, coef: coef * 10, exp: exp - 1})
      end

  def to_integer(%Decimal{sign: sign, coef: coef, exp: exp} = d)
      when is_integer(coef) and exp < 0 and Kernel.rem(coef, 10) == 0 do
        IO.inspect(d, label: "Branch 3", structs: false)
        to_integer(%Decimal{sign: sign, coef: Kernel.div(coef, 10), exp: exp + 1})
      end
end

MyDecimal.to_integer(Decimal.new("1234.50"))

and it prints

Branch 3: %{__struct__: Decimal, coef: 123450, exp: -2, sign: 1}

** (FunctionClauseError) no function clause matching in MyDecimal.to_integer/1

The following arguments were given to MyDecimal.to_integer/1:

    # 1
    #Decimal<1234.5>

    #cell:2: MyDecimal.to_integer/1

So, the “problem” occurs when trying to do to_integer(%Decimal{sign: 1, coef: 12345, exp: -1}).

Maybe Decimal does not want you to do anything ambiguous about the fraction part.

Also Liked

kip

kip

ex_cldr Core Team

Thanks very much for following this through. And spotting my silly error - which is to not notice that the third clause is recursive (as it needs to be). I know that the number can’t be converted to an integer, I just couldn’t see why it wouldn’t match on the third clause. And indeed it does match on the first pass. I missed that it was recursing and not matching on a later pass (in fact there is an accepted PR that handles this error case better then the published version).

Thanks again.

wojtekmach

wojtekmach

Hex Core Team

I think latest Decimal should have a proper error message. Or maybe that change wasn’t released yet. If still have a bad error message, that’s definitely a bug and please report it!

Where Next?

Popular in Questions Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
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
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
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

Other popular topics Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30048 115
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
Tee
can someone please explain to me how Enum.reduce works with maps
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
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
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
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
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; someth...
New

We're in Beta

About us Mission Statement