Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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.
  • Loading branch information...
commit ff43c30f0aeaa66afa9cafe27788de309ff3dedb 1 parent bac15e5
@timhunt timhunt authored
View
8 question/type/numerical/question.php
@@ -320,10 +320,13 @@ public function get_tolerance_interval() {
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 @@ public function get_tolerance_interval() {
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':
View
42 question/type/numerical/tests/answer_test.php
@@ -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 @@ public function test_within_tolerance_nominal() {
$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() {
Please sign in to comment.
Something went wrong with that request. Please try again.