I have a problem with round

Background
I’m learning Elixir by making exercises in codewars. I have a problem with the rounding of the numbers, I can not know what type to use.

Exercise
All we eat is water and dry matter.

John bought potatoes: their weight is 100 kilograms. Potatoes contain water and dry matter.

The water content is 99 percent of the total weight. He thinks they are too wet and puts them in an oven - at low temperature - for them to lose some water.

At the output the water content is only 98%.

What is the total weight in kilograms (water content plus dry matter) coming out of the oven?

He finds 50 kilograms and he thinks he made a mistake: “So much weight lost for such a small change in water content!”

Can you help him?

Write function potatoes with

  • int parameter p0 - initial percent of water-
  • int parameter w0 - initial weight -
  • int parameter p1 - final percent of water -

potatoes should return the final weight coming out of the oven w1 truncated as an int.

Example:

potatoes(99, 100, 98) --> 50

This is my solution

def potatoes(p0, w0, p1) do
  ig = w0 - w0 * p0/100
  p2 = p1/100 * w0
  x = p2-w0
  trunc(ig/x * w0 * -1)
end

Problem

The problem comes when you have to round, I do not know at what time or what function to use for this

Ex:
code: assert Potatoes.potatoes(p0, w0, p1) == exp
left: 38
right: 39

Question
I don’t know what function use to round in this case

It is very difficult to help you without more information; can you explain in more detail:

  • What the problem is?
  • What you’ve already tried to solve it?
  • What you are specifically stuck at in solving it right now?
1 Like

Just leave that one alone.

They do not specify how to deal with intermediate values or which precision the need to have.

This is not the first codewars exercise that brought up here and caused trouble because of underspecifying the rounding and/or precision to use.

The most recent one was Codewars Tortoise racing IIRC.

2 Likes

I am actually to blame here.

He came to me with this exercise and I noticed he was using the / operator, which adds imprecision. I know Elixir has div and trunc but after trying them out I couldn’t make the problem pass the final tests, so I told him to ask for help in here where people who are more familiar with these operators and functions could provide some insight …

He will be updating the description soon, to make it more clear. Knowing how to ask is also important.

2 Likes

As floats are used somewhere in the intermediate steps, we need to know the code that checks the random tests, to give an answer that suites those.

From the last time we know, that CW is not interested in giving away those information or making their tests more tolerant.

The proper way, were to ask for a float result and checking if it is close enough to the expected value.

Usually for those a constant epsilon is defined somewhere and tests look similar to this:

test "value is close to expectation" do
  assert abs(exp - calc()) <= @epsilon
end

This is one of the first things one learns about testing with float, and even when doing it like this, things can get wrong, as I learnt in a computer graphics assignment.

Even though the unit test for my function was correct, I calculated consecutive values differently from the examples, such that the image I produced was different by a single pixel than the expected image.

This was because I did something similar to row * the_function(…), while the expected image was created with something like old_value += the_function(…). This way errors distributed differently for both implementations, resulting in a single pixel beeing #010101 instead of #000000

Disclaimer: Because this was the last issue with my generated images, and the instructor also said from a first glance my code were correct so they helped me to figure this out and from then on they described algorithms that loop over floats in a more strict way.

4 Likes

There is even assert_in_delta exactly for that.

2 Likes

The issue here is that you are using the operator / too many times.
Ideally, for these types of mathematical exercises, one should avoid the usage of such operators at all costs.

Is this the correct way of teaching someone? I don’t think so, but that is a discussion for another time.

Following the previous premise, the ideal solution to this problem would only use the + and - operators.
However we will have to divide at some point. There are a myriad of solutions using only the div operator once but I was unable to get to them.

The solution I got was the following:

defmodule Potatoes do

  @spec potatoes(integer, integer, integer) :: integer
  def potatoes(initial_humidity, initial_weight, final_humidity) do
    num = 100 - initial_humidity
    den = 100 - final_humidity
        
    trunc( initial_weight * (num/den) )
  end
    
end

Hope it helped.

3 Likes