I am doing the following programming exercise: Drying Potatoes. The statement is:
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
I have tried to figure out how could we calculate the final weight, by hand. Plus, I got help in the following thread: How could we calculate the final weight, knowing the initial and final water percentage, and initial weight‽??
I have written the following code:
class Potatoes {
public static long potatoes(int p0, int w0, int p1) {
System.out.println("\ninitial percent of water p0: "+p0);
System.out.println("initial weight w0: "+w0);
System.out.println("final percent of water p1: "+p1);
double left = (1 + (p1 / (100.0 - p1)));
System.out.println("left: "+left);
double right = (w0 * (1 - (p0/100.0)));
System.out.println("right: "+right);
System.out.println("double result: "+left * right);
System.out.println("int result: "+(int)(left * right));
return (int)(left * right);
}
}
And I have observed that there are some tests where the output is different than expected. The difficulty is that the statement requires us to truncate the final weight to int. But if we do that, there are some edge cases, where the left * right
instruction returns a number with a lot of decimals like: .9999999; and it expects the next int (to round it up).
It is better explained with the test cases themselves:
import static org.junit.Assert.*;
import org.junit.Test;
public class PotatoesTest {
private static void dotest(int p0, int w0, int p1, int expected) {
assertEquals(expected, Potatoes.potatoes(p0, w0, p1));
}
@Test
public void testResultDoesNotNeedRounding() {
dotest(99, 100, 98, 50);
dotest(82, 127, 80, 114);
dotest(93, 129, 91, 100);
}
@Test
public void testResultNeedsRoundingUp1(){
dotest(92, 120, 88, 80);
}
@Test
public void testResultNeedsRoundingUp2(){
dotest(91, 132, 89, 108);
}
}
When we execute the previous tests, the console gives us the following output:
initial percent of water p0: 99
initial weight w0: 100
final percent of water p1: 98
left: 50.0
right: 1.0000000000000009
double result: 50.00000000000004
int result: 50
initial percent of water p0: 82
initial weight w0: 127
final percent of water p1: 80
left: 5.0
right: 22.860000000000007
double result: 114.30000000000004
int result: 114
initial percent of water p0: 93
initial weight w0: 129
final percent of water p1: 91
left: 11.11111111111111
right: 9.029999999999994
double result: 100.33333333333326
int result: 100
initial percent of water p0: 92
initial weight w0: 120
final percent of water p1: 88
left: 8.333333333333332
right: 9.599999999999994
double result: 79.99999999999994
int result: 79
expected:<80> but was:<79>
initial percent of water p0: 91
initial weight w0: 132
final percent of water p1: 89
left: 9.090909090909092
right: 11.879999999999995
double result: 107.99999999999997
int result: 107
expected:<108> but was:<107>
So, as you see, the first three tests pass, because of they do not need to be rounded and can be truncated to int. However the two last fail, because of the exercise expects them to round it up.
Besides I have written the following to be able to pass the test cases, however I know there should be a better approach:
class Potatoes {
public static long potatoes(int p0, int w0, int p1) {
System.out.println("\ninitial percent of water p0: "+p0);
System.out.println("initial weight w0: "+w0);
System.out.println("final percent of water p1: "+p1);
double left = (1 + (p1 / (100.0 - p1)));
System.out.println("left: "+left);
double right = (w0 * (1 - (p0/100.0)));
System.out.println("right: "+right);
System.out.println("double result: "+left * right);
System.out.println("int result: "+(int)(left * right));
return String.valueOf(left * right).contains(".99") ? (int)(Math.ceil(left * right)) : (int)(left * right);
}
}
In addition, I have read:
- How to round a number to n decimal places in Java
- Round a double to 2 decimal places
- round up to 2 decimal places in java?
- Java Round up Any Number
How could we round a decimal up if it ends in 0.99‽??
EDIT: I have tested and Math.round does not solve this situation. Here we have two more test cases:
import static org.junit.Assert.*;
import org.junit.Test;
public class PotatoesTest {
private static void dotest(int p0, int w0, int p1, int expected) {
assertEquals(expected, Potatoes.potatoes(p0, w0, p1));
}
@Test
public void mathRoundDoesNotWork1(){
dotest(89,53,85,38);
}
@Test
public void mathRoundDoesNotWork2(){
dotest(82,134,77,104);
}
}
Being the code:
class Potatoes {
public static long potatoes(int p0, int w0, int p1) {
System.out.println("\ninitial percent of water p0: "+p0);
System.out.println("initial weight w0: "+w0);
System.out.println("final percent of water p1: "+p1);
double left = (1 + (p1 / (100.0 - p1)));
System.out.println("left: "+left);
double right = (w0 * (1 - (p0/100.0)));
System.out.println("right: "+right);
System.out.println("double result: "+left * right);
System.out.println("int result: "+(int)(left * right));
return Math.round(left*right);
}
}
And we see in the output:
initial percent of water p0: 89
initial weight w0: 53
final percent of water p1: 85
left: 6.666666666666667
right: 5.829999999999999
double result: 38.86666666666666
int result: 38
expected:<38> but was:<39>
initial percent of water p0: 82
initial weight w0: 134
final percent of water p1: 77
left: 4.3478260869565215
right: 24.120000000000008
double result: 104.86956521739134
int result: 104
expected:<104> but was:<105>
The question is: how do we round up only if a double result contains a very close number to the next int, meaning it has the format: 100.999999... (number.{9 n decimals})?