MDL-31837 numerical tolerance: better handling of very small tolerances.
The changes between Moodle 1.9 and 2.1 made the marking of very small answers like 10^-20 almost impossible. This change fixes it. This fix is almost entirely due the the careful research of Pierre Pichet, who carefully testing various proposals, and worked out that this one seemed best.
This commit is contained in:
@@ -320,10 +320,13 @@ class qtype_numerical_answer extends question_answer {
|
||||
throw new coding_exception('Cannot work out tolerance interval for answer *.');
|
||||
}
|
||||
|
||||
// Smallest number that, when added to 1, is different from 1.
|
||||
$epsilon = pow(10, -1 * ini_get('precision'));
|
||||
|
||||
// We need to add a tiny fraction depending on the set precision to make
|
||||
// the comparison work correctly, otherwise seemingly equal values can
|
||||
// yield false. See MDL-3225.
|
||||
$tolerance = (float) $this->tolerance + pow(10, -1 * ini_get('precision'));
|
||||
$tolerance = abs($this->tolerance) + $epsilon;
|
||||
|
||||
switch ($this->tolerancetype) {
|
||||
case 1: case 'relative':
|
||||
@@ -331,8 +334,7 @@ class qtype_numerical_answer extends question_answer {
|
||||
return array($this->answer - $range, $this->answer + $range);
|
||||
|
||||
case 2: case 'nominal':
|
||||
$tolerance = $this->tolerance + pow(10, -1 * ini_get('precision')) *
|
||||
max(1, abs($this->answer));
|
||||
$tolerance = $this->tolerance + $epsilon * max(abs($this->tolerance), abs($this->answer), $epsilon);
|
||||
return array($this->answer - $tolerance, $this->answer + $tolerance);
|
||||
|
||||
case 3: case 'geometric':
|
||||
|
||||
@@ -25,8 +25,10 @@
|
||||
*/
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
|
||||
require_once($CFG->dirroot . '/question/type/numerical/question.php');
|
||||
|
||||
|
||||
class qtype_numerical_answer_test extends advanced_testcase {
|
||||
public function test_within_tolerance_nominal() {
|
||||
$answer = new qtype_numerical_answer(13, 7.0, 1.0, '', FORMAT_MOODLE, 1.0);
|
||||
@@ -38,16 +40,46 @@ class qtype_numerical_answer_test extends advanced_testcase {
|
||||
$this->assertFalse($answer->within_tolerance(8.01));
|
||||
}
|
||||
|
||||
public function test_within_tolerance_nominal_zero() {
|
||||
// Either an answer or tolerance of 0 requires special care. We still
|
||||
// don't want to end up comparing two floats for absolute equality.
|
||||
|
||||
// Zero tol, non-zero answer.
|
||||
$answer = new qtype_numerical_answer(13, 1e-20, 1.0, '', FORMAT_MOODLE, 0.0);
|
||||
$this->assertFalse($answer->within_tolerance(0.9999999e-20));
|
||||
$this->assertTrue($answer->within_tolerance(1e-20));
|
||||
$this->assertFalse($answer->within_tolerance(1.0000001e-20));
|
||||
|
||||
// Non-zero tol, zero answer.
|
||||
$answer = new qtype_numerical_answer(13, 0.0, 1.0, '', FORMAT_MOODLE, 1e-24);
|
||||
$this->assertFalse($answer->within_tolerance(-2e-24));
|
||||
$this->assertTrue($answer->within_tolerance(-1e-24));
|
||||
$this->assertTrue($answer->within_tolerance(0));
|
||||
$this->assertTrue($answer->within_tolerance(1e-24));
|
||||
$this->assertFalse($answer->within_tolerance(2e-24));
|
||||
|
||||
// Zero tol, zero answer.
|
||||
$answer = new qtype_numerical_answer(13, 0.0, 1.0, '', FORMAT_MOODLE, 1e-24);
|
||||
$this->assertFalse($answer->within_tolerance(-1e-20));
|
||||
$this->assertTrue($answer->within_tolerance(-1e-35));
|
||||
$this->assertTrue($answer->within_tolerance(0));
|
||||
$this->assertTrue($answer->within_tolerance(1e-35));
|
||||
$this->assertFalse($answer->within_tolerance(1e-20));
|
||||
|
||||
// Non-zero tol, non-zero answer.
|
||||
$answer = new qtype_numerical_answer(13, 1e-20, 1.0, '', FORMAT_MOODLE, 1e-24);
|
||||
$this->assertFalse($answer->within_tolerance(1.0002e-20));
|
||||
$this->assertTrue($answer->within_tolerance(1.0001e-20));
|
||||
$this->assertTrue($answer->within_tolerance(1e-20));
|
||||
$this->assertTrue($answer->within_tolerance(1.0001e-20));
|
||||
$this->assertFalse($answer->within_tolerance(1.0002e-20));
|
||||
}
|
||||
|
||||
public function test_within_tolerance_blank() {
|
||||
$answer = new qtype_numerical_answer(13, 1234, 1.0, '', FORMAT_MOODLE, '');
|
||||
$this->assertTrue($answer->within_tolerance(1234));
|
||||
$this->assertFalse($answer->within_tolerance(1234.000001));
|
||||
$this->assertFalse($answer->within_tolerance(0));
|
||||
|
||||
$answer = new qtype_numerical_answer(13, 0, 1.0, '', FORMAT_MOODLE, '');
|
||||
$this->assertTrue($answer->within_tolerance(0));
|
||||
$this->assertFalse($answer->within_tolerance(pow(10, -1 * ini_get('precision') + 1)));
|
||||
$this->assertTrue($answer->within_tolerance(pow(10, -1 * ini_get('precision'))));
|
||||
}
|
||||
|
||||
public function test_within_tolerance_relative() {
|
||||
|
||||
Reference in New Issue
Block a user