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
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.
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.
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