diff --git a/classes/local/evaluator.php b/classes/local/evaluator.php index a2bdd6c8..9b929738 100644 --- a/classes/local/evaluator.php +++ b/classes/local/evaluator.php @@ -163,6 +163,13 @@ public function substitute_variables_in_text(string $text, bool $skiplists = tru if ($skiplists && in_array($result->type, [token::LIST, token::SET])) { continue; } + // If the result is a numeric string, we convert it back to a number, in order for + // the standard PHP formatting to apply: numbers with an absolute value ≥ 1e14 + // or < 0.0001 would normally be printed in scientific notation. + if ($result->type === token::STRING && is_numeric($result->value)) { + $result->type = token::NUMBER; + $result->value = floatval($result->value); + } // If the result is a number, we try to localize it, unless the admin settings do not // allow the decimal comma. if ($result->type === token::NUMBER) { diff --git a/tests/evaluator_test.php b/tests/evaluator_test.php index 98cd0cc5..0be7f2fc 100644 --- a/tests/evaluator_test.php +++ b/tests/evaluator_test.php @@ -1348,6 +1348,33 @@ public function test_substitute_variables_in_text(): void { self::assertEquals($expected, $output); } + public function test_substitute_variables_in_text_with_sigfig(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Define, parse and evaluate some variables. + $vars = 'a=0.3; b=sigfig(a, 2); c=0.000005; d=sigfig(c, 8); e=fact(19); f=sigfig(e, 3)'; + $parser = new parser($vars); + $statements = $parser->get_statements(); + $evaluator = new evaluator(); + $evaluator->evaluate($statements); + + // We only test the correct replacement of the numbers; correct interpretation of + // placeholders is covered in another test. + $text = '{a} -- {b} -- {c} -- {d} -- {e} -- {f}'; + $output = $evaluator->substitute_variables_in_text($text); + $expected = '0.3 -- 0.3 -- 5.0E-6 -- 5.0E-6 -- 1.2164510040883E+17 -- 1.22E+17'; + self::assertEquals($expected, $output); + + // Setting the localised decimal separator, but disallow the decimal comma in the admin settings. + qtype_formulas_test_helper::define_local_decimal_separator(); + set_config('allowdecimalcomma', 1, 'qtype_formulas'); + self::assertEquals('1', get_config('qtype_formulas', 'allowdecimalcomma')); + $output = $evaluator->substitute_variables_in_text($text); + $expected = '0,3 -- 0,3 -- 5,0E-6 -- 5,0E-6 -- 1,2164510040883E+17 -- 1,22E+17'; + self::assertEquals($expected, $output); + } + public function test_substitute_variables_in_algebraic_formula(): void { // Define, parse and evaluate some variables. $vars = 'a=1; b=[2,3,4]; c={1,2,3}; x={1:10}; y={1:10}; k = [[1,2],[3,4]];'; diff --git a/tests/renderer_test.php b/tests/renderer_test.php index 029d00c7..52b6ea6d 100644 --- a/tests/renderer_test.php +++ b/tests/renderer_test.php @@ -288,6 +288,33 @@ public function test_substitution_of_local_variables(): void { $this->check_output_does_not_contain_stray_placeholders(); } + public function test_right_answer_feedback_uses_appropriate_decimal_separator(): void { + // Setting the localised decimal separator, but disallow the decimal comma in the admin settings. + qtype_formulas_test_helper::define_local_decimal_separator(); + self::assertEquals('0', get_config('qtype_formulas', 'allowdecimalcomma')); + + $q = $this->get_test_formulas_question('testsinglenum'); + $q->parts[0]->answer = '3.5'; + + $this->start_attempt_at_question($q, 'immediatefeedback', 1); + $this->process_submission(['0_0' => '42', '-submit' => 1]); + $this->check_output_contains_lang_string('correctansweris', 'qtype_formulas', '3.5'); + + // Now allowing the decimal comma to be used. + set_config('allowdecimalcomma', 1, 'qtype_formulas'); + $this->start_attempt_at_question($q, 'immediatefeedback', 1); + $this->process_submission(['0_0' => '42', '-submit' => 1]); + $this->check_output_contains_lang_string('correctansweris', 'qtype_formulas', '3,5'); + + // Make sure the decimal comma is also applied for numbers that are string tokens. + // Note that we *should* have 3,50 as the model answer, but as we are converting + // the string output from sigfig() back to a number, trailing zeroes will be lost. + $q = $this->get_test_formulas_question('testsinglenum'); + $q->parts[0]->answer = 'sigfig(3.5, 3)'; + $this->process_submission(['0_0' => '42', '-submit' => 1]); + $this->check_output_contains_lang_string('correctansweris', 'qtype_formulas', '3,5'); + } + public function test_render_question_with_separate_unit_field(): void { $q = $this->get_test_formulas_question('testsinglenumunitsep'); $q->parts[0]->unitpenalty = 0.5;