java
java copied to clipboard
[Java track] Tests for exercise `rational-numbers` are flaky
Track: Java Exercise rational-numbers Problem: The one or more tests for the exercise are flaky/non-deterministic
The test case testRaiseARealNumberToAPositiveRationalNumber, fails when I run it locally but passes when submitted to the exercism build servers.
vscode ➜ /workspaces/java $ gradle rational-numbers:test
> Task :rational-numbers:test FAILED
RationalTest > testReduceARationalNumberWithANegativeDenominatorToLowestTerms() PASSED
RationalTest > testSubtractTwoNegativeRationalNumbers() PASSED
RationalTest > testRaiseANegativeRationalNumberToAPositiveIntegerPower() PASSED
RationalTest > testReducePlacesTheMinusSignOnTheNumerator() PASSED
RationalTest > testMultiplyARationalNumberByZero() PASSED
RationalTest > testAbsoluteValueOfAPositiveRationalNumberWithNegativeNumeratorAndDenominator() PASSED
RationalTest > testAbsoluteValueOfANegativeRationalNumber() PASSED
RationalTest > testAddARationalNumberToItsAdditiveInverse() PASSED
RationalTest > testRaiseZeroToAnIntegerPower() PASSED
RationalTest > testRaiseANegativeRationalNumberToAnEvenNegativeIntegerPower() PASSED
RationalTest > testReduceOneToLowestTerms() PASSED
RationalTest > testMultiplyARationalNumberByItsReciprocal() PASSED
RationalTest > testDivideAPositiveRationalNumberByANegativeRationalNumber() PASSED
RationalTest > testRaiseAPositiveRationalNumberToANegativeIntegerPower() PASSED
RationalTest > testReduceANegativeRationalNumberToLowestTerms() PASSED
RationalTest > testRaiseOneToAnIntegerPower() PASSED
RationalTest > testRaiseANegativeRationalNumberToAnOddNegativeIntegerPower() PASSED
RationalTest > testAddTwoPositiveRationalNumbers() PASSED
RationalTest > testDivideTwoPositiveRationalNumbers() PASSED
RationalTest > testAddAPositiveRationalNumberAndANegativeRationalNumber() PASSED
RationalTest > testRaiseARealNumberToAPositiveRationalNumber() FAILED
java.lang.AssertionError:
Expecting actual:
16.0
to be close to:
15.999999999999998
by less than 1.0E-15 but difference was 2.0E-15.
(a difference of exactly 1.0E-15 being considered valid)
at RationalTest.assertDoublesEqual(RationalTest.java:13)
at RationalTest.testRaiseARealNumberToAPositiveRationalNumber(RationalTest.java:246)
RationalTest > testDivideARationalNumberByOne() PASSED
RationalTest > testSubtractARationalNumberFromItself() PASSED
RationalTest > testMultiplyTwoPositiveRationalNumbers() PASSED
RationalTest > testAbsoluteValueOfZero() PASSED
RationalTest > testRaiseARealNumberToANegativeRationalNumber() PASSED
RationalTest > testReduceAPositiveRationalNumberToLowestTerms() PASSED
RationalTest > testMultiplyANegativeRationalNumberByAPositiveRationalNumber() PASSED
RationalTest > testSubtractTwoPositiveRationalNumbers() PASSED
RationalTest > testAbsoluteValueOfARationalNumberIsReducedToLowestTerms() PASSED
RationalTest > testMultiplyARationalNumberByOne() PASSED
RationalTest > testAbsoluteValueOfANegativeRationalNumberWithNegativeDenominator() PASSED
RationalTest > testAddTwoNegativeRationalNumbers() PASSED
RationalTest > testDivideTwoNegativeRationalNumbers() PASSED
RationalTest > testRaiseAPositiveRationalNumberToAPositiveIntegerPower() PASSED
RationalTest > testAbsoluteValueOfAPositiveRationalNumber() PASSED
RationalTest > testMultiplyTwoNegativeRationalNumbers() PASSED
RationalTest > testRaiseAPositiveRationalNumberToThePowerOfZero() PASSED
RationalTest > testReduceAnIntegerToLowestTerms() PASSED
RationalTest > testSubtractAPositiveRationalNumberAndANegativeRationalNumber() PASSED
RationalTest > testReduceZeroToLowestTerms() PASSED
41 tests completed, 1 failed
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':rational-numbers:test'.
> There were failing tests. See the report at: file:///workspaces/java/rational-numbers/build/reports/tests/test/index.html
* Try:
> Run with --scan to get full insights.
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
For more on this, please refer to https://docs.gradle.org/8.9/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
BUILD FAILED in 1s
3 actionable tasks: 2 executed, 1 up-to-date
The implementation is the same as pretty much everyone else have submitted
double exp(double exponent) {
return Math.pow(Math.pow(exponent, this.numerator), 1.0 / this.denominator);
}
I have been banging my head against the table for quite at while due to this and needless to say, that is no fun. I do not have any solution for this but I suggest that the tests are rewritten to eliminate the current inherent unreliability. Given the current amount of submissions for the exercise on the track I would be quite surprised if no one but me has encountered the issue.
To be fair, a member already pointed this out while solving the exercise at https://exercism.org/tracks/java/exercises/rational-numbers/solutions/nunoguedelha
/* exponent^(a/b) = (exponent^a)^(1/b)
If we compute ^(1/b) using the Math.pow function, we cannot a priori fully control the accuracy.
Typically, we might not reach the accuracy of 1e-15 as the exercise tests require. For instance:
Math.pow(8.0, 1.0/3) returns the exact result 2.0, such that if we compute 8^(4/3) as
Math.pow(Math.pow(8.0, 1.0/3), 4) we get the exact result 16.0, but if we compute it as
Math.pow(Math.pow(8.0, 4), 1.0/3) we get 15.999999999999998 which has an accuracy of 2e-15 > 1e-15,
and this causes the test to fail.
But in general, Math.pow(Math.pow(x, 4), 1.0/3) should provide a better accuracy since only the
last operation generates an approximation error. Instead, doing it the other way around amplifies
the error of the first operation.
The example Math.pow(Math.pow(8, 1.0/3), 4) is a lucky case, but the simple case Math.pow(4096, 1.0/3)
illustrates well the problem.
Computing ^(1/b) with the Heron's method allows us to better control the accuracy and reach the desired
1e-15.
*/