public static boolean almostEqual(double a, double b, double eps){
return Math.abs(a-b)<eps;
}
Where eps is measure of equality.
Answer from Ano on Stack Overflowpublic static boolean almostEqual(double a, double b, double eps){
return Math.abs(a-b)<eps;
}
Where eps is measure of equality.
You must first decide what "almost the same" means. For example, there's a method in java.lang.Math called ulp() which, given a double, returns the distance between that double and the next; i.e., the smallest possible difference between that number and any other. You might simply compare the difference between the two doubles and the result of calling that method.
On the other hand, maybe you want two numbers to just be within 1% of eachother. In that case, do the same computation, but use the first number multiplied by 0.01 instead of ulp() as the largest acceptable distance.
This is often need when using doubles as you can get small rounding errors.
if(Math.abs(lineNumber - startLineNumber) <= 2)
You can change 2 to 5. This is says; if the difference between two values is less than 2. i.e it could be -2, -1, 0, 1 or 2.
For double a similar solution is to test "equality" using
if(Math.abs(a - b) < ERR) // where ERR is 1e-4 or 1e-9
What is a good value of ERR to use depends on the situation.
You are interested in the value difference. You don't care what value is higher, you are just interested in they difference. That we can obtain by lineNumber - startLineNumber. That is nice, but it produces positive as well as negative values. So when we you absolute value of this difference we have their distance which can be used for comparison.
if ( Math.abs( lineNumber - startLineNumber ) <= 10 ) {
// values are in tolerance -10 .. +10, ... 21 values
// the range of tolerance in now simple changeable by one number
// .. the distance of values, which can be defined
// as a constant wherever you want,
// static final field, property file, ..
}
- Please make the class final to let noone inherit the class.
- Nobody would expect a
AsserationErrorif he can inherit the class. - Everyone who inherit the class would have duplicate javadoc.
- Nobody would expect a
- Please throw a
RuntimeExceptionor anIllegalStateExceptionbecause the javadoc matches better. ;Plet me miss seriousness.- i miss
strictfpat the class definition because the math-processor may wrong. getFloatToleranceyou dont need the getter. If it would be a bean, you need it but it is no bean.setFloatTolerancethrows a IAE, but this behaviour is not part of the javadoc, and if you add this behaviour to the javadoc you shall write the signature of the method matching to the javadoc.assertProperLengthshould be have the correct javadoc and signature too (including the pending methods).- The classname
Numbersshould be named "NumberHelper". The above code runs without resulting in any assertion errors. (Yipee!!), Yipee but wait ... did you run it as JUnit-Tests or activated the-eaby manual?- Some methods needs to have javadoc.
I have a few more but ill stop here because this class is not thread-save.
Generic methods
You can use generic methods for something like this. Rather than having to type out methods for each individual integer type, you can use a generic method for your areEqual methods, like this:
public static <TNumber> boolean areEqual(TNumber... numbers) {
int length = numbers.length;
assertProperLength(length);
TNumber d = numbers[0];
for (int i = 1; i < length; i++) {
if (!d.equals(numbers[i])) {
return false;
}
}
return true;
}
This eliminates the need to create duplicate methods for each individual type, and makes your code generally easier to read.
This can also apply to your areEqual overloads with just two arguments. I wasn't quite sure how to implement them in this context, although I'm quite sure you can figure it out. Be sure to use .equal though rather than ==, as areEqual may return false when true is expected.
Nitpicks
Some of your error messages are not so great. For example:
"No instances for you! ;P"would be better as"An instance of this class cannot be created."."Absurd tolerance value."would also be better as"Invalid tolerance value. Tolerance value must be ...".
The error types that you're raising don't make much sense. For example, instead of an IllegalArgumentException, it might make more sense to raise an ArithmeticException.
I would use a library for this, the one I normally use is DoubleMath fro Googles Guava library. https://google.github.io/guava/releases/19.0/api/docs/com/google/common/math/DoubleMath.html
if (DoubleMath.fuzzyEquals(a, b, epsilon)) {
// a and b are equal within the tolerance given
}
there is also a fuzzyCompare.
You can use the class org.apache.commons.math3.util.Precision from the Apache Commons Math. Example:
if (Precision.equals(sum, price, 0.009)) {
// arguments are equal or within the range of allowed error (inclusive)
}
1) Don't override equals/hashCode only for unit testing purposes
These methods have a semantic and their semantic is not taking into consideration all fields of the class to make a test assertion possible.
2) Rely on testing library to perform your assertions
Assert(students.contains(expectedStudent)
or that (posted in the John Bollinger answer):
Assert(students.stream().anyMatch(s -> expectedStudent.matches(s)));
are great anti patterns in terms of unit testing.
When an assertion fails, the first thing that you need is knowing the cause of the error to correct the test.
Relying on a boolean to assert the list comparison doesn't allow that at all.
KISS (Keep it simple and stupid): Use testing tools/features to assert and don't reinvent the wheel because these will provide the feedback needed when your test fails.
3) Don't assert double with equals(expected, actual).
To assert double values, unit testing libraries provide a third parameter in the assertion to specify the allowed delta such as :
public static void assertEquals(double expected, double actual, double delta)
in JUnit 5 (JUnit 4 has a similarly thing).
Or favor BigDecimal to double/float that is more suitable for this kind of comparison.
But it will not completely solve your requirement as you need to assert multiple fields of your actual object. Using a loop to do that is clearly not a fine solution.
Matcher libraries provide a meaningful and elegant way to solve that.
4) Use Matcher libraries to perform assertions on specific properties of objects of the actual List
With AssertJ :
//GIVEN
...
//WHEN
List<Student> students = getStudents();
//THEN
Assertions.assertThat(students)
// 0.1 allowed delta for the double value
.usingComparatorForType(new DoubleComparator(0.1), Double.class)
.extracting(Student::getId, Student::getName, Student::getGpa)
.containsExactly(tuple(1234, "Peter Smith", 3.89),
tuple(...),
);
Some explanations (all of these are AssertJ features) :
usingComparatorForType()allows to set a specific comparator for the given type of elements or their fields.DoubleComparatoris a AssertJ comparator providing the facility to take an epsilon into consideration in the double comparison.extractingdefines values to assert from the instances contained in the List.containsExactly()asserts that the extracted values are exactly (that is no more, no less and in the exact order) these defined in theTuples.
The behavior of List.contains() is defined in terms of the equals() methods of the elements. Therefore, if your Student.equals() method compares gpas for exact equality and you cannot change it then List.contains() is not a viable method for your purpose.
And probably Student.equals() shouldn't use a comparison with tolerance, because it's very hard to see how you could make that class's hashCode() method consistent with such an equals() method.
Perhaps what you can do is write an alternative, equals-like method, say "matches()", that contains your fuzzy-comparison logic. You could then test a list for a student fitting your criteria with something like
Assert(students.stream().anyMatch(s -> expectedStudent.matches(s)));
There is an implicit iteration in that, but the same is true of List.contains().
IBM has a recommendation for comparing two floats, using division rather than subtraction - this makes it easier to select an epsilon that works for all ranges of input.
if (abs(a/b - 1) < epsilon)
As for the value of epsilon, I would use 5.96e-08 as given in this Wikipedia table, or perhaps 2x that value.
It wants you to compare them to within the amount of accuracy you need. For example if you require that the first 4 decimal digits of your floats are equal, then you would use:
if(-0.00001 <= a-b && a-b <= 0.00001)
{
..
}
Or:
if(Math.abs(a-b) < 0.00001){ ... }
Where you add the desired precision to the difference of the two numbers and compare it to twice the desired precision.
Whatever you think is more readable. I prefer the first one myself as it clearly shows the precision you are allowing on both sides.
a = 5.43421 and b = 5.434205 will pass the comparison