Question

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.

Was it helpful?

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.

OTHER TIPS

Your equal method should ideally test every necessary value in your class. In this case, it should be checking to see if your left and right values are the same between the two objects. If they are the same, then you can consider the objects to be equal.

In your case, you should probably put a debug point in your equals method to see why the function is returning back a false.

Try using Eclipse's built in functionality to create equals and hashcode methods for you. You can create that by going to Source->Generate hashCode() and equals(). The methods will be very different from what you have created.

Another thing, in your AssertEquals method, make sure both the values passed in are of the same type. In your case, you're checking a Double with an ExactNumber object. They will definitely not be the same. You need to either

  1. Change your Add method to return a ExactNumber object
  2. Have a method in your ExactNumber class called getDouble() and use that as the second parameter instead.

Hope this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top