Search code examples
javajunit3

Test Case failing when expected is equal to output


Before I get into detail, YES this is a HOMEWORK ASSIGNMENT. NO I DON'T WANT ANSWERS, JUST TIPS and/or Suggestions to try this or that.

The problem introduces with this:

Create a class, ExactNumber, that uses two long properties named left and right (representing the portion of the number that is to the left and right of the decimal point respectively). For example, 3.75 would be represented by new ExactNumber(3, 7500000000000000L). Note the L on the end which tells Java the large number is a long. This translates to: 3 + 7500000000000000/10000000000000000 = 3.75

Here is my code:

public class ExactNumber {
    private long left;
    private long right;

    public ExactNumber(long left, long right) {
        this.left = left;
        this.right = right;
    }

    public String toString() {
        return String.valueOf(doubleValue());
    }

    public double doubleValue() {
        return ((double) left +  (double) (right/ 100000000000000L) / 100);
    }

    public int compareTo (ExactNumber exactNumber) {
        if(exactNumber.left < left) {
            return 1;
        }
        else if (exactNumber.left == left) {
            if (exactNumber.right < right) {
                return 1;
            }
            else if (exactNumber.right == right) {
                return 0;
            }
            else {
                return -1;
            }
        }
        else {
            return -1;
        }
    }

    public boolean equal(ExactNumber thisobject) {
        if (thisobject instanceof ExactNumber) {
            if (thisobject.doubleValue() == this.doubleValue()) {
                return true;
            }
            else {
                return false;
            }
        }
        else {
            return false;
        }
    }

    public double add(ExactNumber exactNumber) {;
        return ((left+exactNumber.left) + (double)((right+exactNumber.right)*1E-16));
    }
}

My problem are the tests coming up as an error when the expected value is equal to the actual value. Here are the test cases (NOTE: there are more test cases, but they pass the JUnit test):

public class TestExactNumber extends TestCase {
    ExactNumber threesevenfive = new ExactNumber(3, 7500000000000000L);
    ExactNumber threesevenfive_andalittlebit = new ExactNumber(3, 7500000000000001L);
    ExactNumber threesevenfive_dupe = new ExactNumber(3, 7500000000000000L);
    ExactNumber ten = new ExactNumber(10, 0);
    ExactNumber thirteensevenfive = new ExactNumber(13, 7500000000000000L);
    ExactNumber sevenfifty = new ExactNumber(7, 5000000000000000L);

public void test_equals() {
        assertFalse(threesevenfive.equals(threesevenfive_andalittlebit));
        assertEquals(threesevenfive, threesevenfive_dupe);
    }

public void test_add() {
        assertEquals(threesevenfive.add(ten), thirteensevenfive);
        assertEquals(threesevenfive.add(threesevenfive), sevenfifty);

The assertEquals above failed in the JUnit test, but says like (for an example) expected = 13.75 and actual = 13.75.

Any tips or hints at what I need to do with my code is greatly appreciated. And thank you in advanced.

NOTES:

  • According to my instructor, I should not be using the doubleValue method to implement my equals method. I know that I do have it in my code, but that was prior to the tip the instructor gave me and I am just unsure about how to change it.

  • I am using eclipse for java to code this.


Solution

  • Your equal Method is never used. The Java Method used by assertEquals() is called equalS (and you have to override the equals() method derived from Object). Therefore, the assertion will use equals inherited from Object, which will compare the actual instances rather than using YOUR equal method which will compare the objet values. And since they are two different INSTANCES, they are not equal.

    Finally, the two instances will be plotted with toString() resulting in expected = 13.75 and actual = 13.75. (Because your toString() returns only the values, ignoring the difference between instances)


    Your Instructors Response: A Long in Java is a 64 bit long number. Double in Java is implemented with the IEEE754 Standard, which only leaves 52 bit for the mantissa. Meaning: Any conversion of a Long Number to a double, where the Long Number has set bits on bit 53 to 63 - will cause the exponent to be shifted in a way, that you loose precision arround the LSBs - resulting in an unprecice Double Value.

    Therefore comparing the double values to determine equality is not sufficent for your desired Design of a "Exact Number".

    Example:

    Long bigLong = 1L<<51; //picked 51: 52 and 53 already causing rounding issues.
    Long long1 = bigLong + 1L;
    Long long2 = bigLong + 2L;
    System.out.println(long1+" -> " + long1.doubleValue());
    System.out.println(long2+" -> " + long2.doubleValue());
    
    //false, enough precision to preserve bit "0" and "1".
    System.out.println(long1.doubleValue()==long2.doubleValue()); 
    

    Output:

    2251799813685262 -> 2.251799813685262E15
    2251799813685263 -> 2.251799813685263E15
    false
    

    When setting bit 54:

    Long bigLong = 1L<<54;
    Long long1 = bigLong + 1L;
    Long long2 = bigLong + 2L;
    System.out.println(long1+" -> " + long1.doubleValue());
    System.out.println(long2+" -> " + long2.doubleValue());
    System.out.println(long1.doubleValue()==long2.doubleValue());
    

    Output:

    18014398509481985 -> 1.8014398509481984E16
    18014398509481986 -> 1.8014398509481984E16
    true
    

    Note the Exponent beeing increased from 15 to 16, which will cut off the difference of "1" between both longs.

    To solve this, you can compare left1 to left2 and right1 to right2 without converting it to a double.