当预期的测试用例失败时等于输出



在我进入细节之前,是的,这是一个家庭作业。不,我不想要答案,只是提示和/或建议,尝试这个或那个。

问题由此产生:

创建一个类ExactNumber,它使用两个名为left的长属性右边(表示数字在左边的部分和小数点右边)。例如,3.75用new ExactNumber(3,7500000000000000l)表示。注意上面的L它告诉Java大数是一个长。这就意味着To: 3 + 750000000000000/10000000000000000 = 3.75

下面是我的代码:
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));
    }
}

我的问题是,当期望值等于实际值时,测试会出现错误。下面是测试用例(注意:还有更多的测试用例,但是它们通过了JUnit测试):

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);

上面的assertEquals在JUnit测试中失败,但表示(例如)expected = 13.75 and actual = 13.75。

任何提示或提示,我需要做我的代码是非常感谢。提前谢谢你。

指出:

  • 根据我的导师,我不应该使用doubleValue方法来实现我的equals方法。我知道我的代码中确实有它,但那是在教练给我的提示之前,我只是不确定如何更改它。

  • 我正在使用eclipse for java编写此代码。

您的equal方法从未使用过。assertEquals()使用的Java方法称为equalS(您必须使用从Object派生的equals()方法override)。因此,断言将使用从Object继承的equals,它将比较实际的instances,而不是使用您的equal方法来比较对象值。因为它们是两个不同的实例,所以它们不相等。

最后,用toString()绘制两个实例,得到expected = 13.75 and actual = 13.75.(因为您的toString()只返回值,忽略实例之间的差异)


你的导师回应:Java中的Long是64位长的数字。Java中的Double是通过IEEE754标准实现的,它只给尾数留下52位。含义:任何长数到双精度数的转换,如果长数在53位到63位上有设置位,将导致指数以某种方式移位,从而在lsb周围失去精度-导致不精确的双精度值。

因此,比较双精度值以确定相等性对于您期望的"精确数字"设计是不够的。

的例子:

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()); 
输出:

2251799813685262 -> 2.251799813685262E15
2251799813685263 -> 2.251799813685263E15
false

设置位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());
输出:

18014398509481985 -> 1.8014398509481984E16
18014398509481986 -> 1.8014398509481984E16
true

注意指数从15增加到16,这将切断两个长数之间的"1"之差。

要解决这个问题,可以将left1left2进行比较,将right1right2进行比较,而无需将其转换为双精度类型。

理想情况下,equal方法应该测试类中的每个必要值。在这种情况下,它应该检查两个对象之间的左值和右值是否相同。如果它们是相同的,那么你可以认为这两个对象是相等的。

在你的例子中,你可能应该在你的equals方法中放置一个调试点,看看为什么函数返回false。

尝试使用Eclipse的内置功能为您创建equals和hashcode方法。您可以通过Source->Generate hashCode()和equals()来创建它。这些方法将与您创建的方法非常不同。

另一件事,在你的AssertEquals方法中,确保传入的两个值是相同类型的。在您的例子中,您正在使用ExactNumber对象检查Double。它们肯定不会是一样的。您需要

  1. 改变你的Add方法返回一个ExactNumber对象
  2. 在你的ExactNumber类中有一个叫做getDouble()的方法,并使用它作为第二个参数。

最新更新