Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions classes/local/evaluator.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,9 @@ 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 number, we try to localize it, unless the admin settings do not
// If the result is a number or a numeric string, we try to localize it, unless the admin settings do not
// allow the decimal comma.
if ($result->type === token::NUMBER) {
if ($result->type === token::NUMBER || ($result->type === token::STRING && is_numeric($result->value))) {
$result = qtype_formulas::format_float($result->value);
}

Expand Down
18 changes: 17 additions & 1 deletion classes/local/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,7 @@ public static function sigfig($number, $precision): string {
get_string('error_func_second_posint', 'qtype_formulas', 'sigfig()'),
self::POSITIVE | self::INTEGER
);
$originalnumber = $number;
$number = floatval($number);
$precision = intval($precision);

Expand All @@ -657,7 +658,22 @@ public static function sigfig($number, $precision): string {
// We only request decimals if $digitsafter is greater than zero.
$digitsafter = max(0, $digitsafter);

return number_format($number, $digitsafter, '.', '');
// If the absolute value of the *original* number is >= 1e-4 and < 1e14, we can format
// it and return a string. The same is true if the rounded number is zero.
if ($number == 0 || (abs($originalnumber) >= 1e-4 && abs($originalnumber) < 1e14)) {
return number_format($number, $digitsafter, '.', '');
}

// If the absolute value of the original number was below 1e-4, but just reached that
// limit after rounding, it should be output as 0.0001, plus possibly some trailing zeroes.
if (abs($originalnumber) < 1e-4 && abs($number) == 1e-4) {
return sprintf("%.{$precision}H", $number);
}

// For all other cases, we force scientific notation. Note that we have to subtract 1
// from the requested precision, because with the E format, the parameter indicates
// the number of places *after* the decimal comma.
return sprintf('%.' . ($precision - 1) . 'E', $number);
}

/**
Expand Down
27 changes: 27 additions & 0 deletions tests/evaluator_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.30 -- 5.0E-6 -- 5.0000000E-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,30 -- 5,0E-6 -- 5,0000000E-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]];';
Expand Down
43 changes: 43 additions & 0 deletions tests/functions_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,49 @@ public static function provide_sigfig_expressions(): array {
['-0.005', 'sigfig(-.005, 1)'],
['-0.0050', 'sigfig(-.005, 2)'],
['-0.00500', 'sigfig(-.005, 3)'],

['9.99E-5', 'sigfig(0.0000999, 3)'],
['9.990E-5', 'sigfig(0.0000999, 4)'],
['1.00E-5', 'sigfig(0.0000100, 3)'],
['1.000E-5', 'sigfig(0.0000100, 4)'],
['-9.99E-5', 'sigfig(-0.0000999, 3)'],
['-9.990E-5', 'sigfig(-0.0000999, 4)'],
['-1.00E-5', 'sigfig(-0.0000100, 3)'],
['-1.000E-5', 'sigfig(-0.0000100, 4)'],
['1.23E-6', 'sigfig(0.000001234, 3)'],
['1.2340E-6', 'sigfig(0.000001234, 5)'],
['-1.23E-5', 'sigfig(-0.0000123, 3)'],
['-1.230E-5', 'sigfig(-0.0000123, 4)'],

['0.000100', 'sigfig(0.0001, 3)'],
['0.0001000', 'sigfig(0.0001, 4)'],
['0.0001', 'sigfig(0.00009995, 3)'],
['9.995E-5', 'sigfig(0.00009995, 4)'],
['-0.000100', 'sigfig(-0.0001, 3)'],
['-0.0001000', 'sigfig(-0.0001, 4)'],
['-0.0001', 'sigfig(-0.00009995, 3)'],
['-9.995E-5', 'sigfig(-0.00009995, 4)'],

['1.00E+14', 'sigfig(1e14, 3)'],
['1.0000E+14', 'sigfig(1e14, 5)'],
['1.23E+14', 'sigfig(1.23e14, 3)'],
['1.2300E+14', 'sigfig(1.23e14, 5)'],
['1.20E+14', 'sigfig(1.2e14, 3)'],
['9.99E+14', 'sigfig(9.99e14, 3)'],
['-1.230E+14', 'sigfig(-1.23e14, 4)'],

['100000000000000', 'sigfig(99999999999999, 3)'],
['100000000000000', 'sigfig(99999999999999, 5)'],
['99900000000000', 'sigfig(99900000000000, 3)'],
['-100000000000000', 'sigfig(-99999999999999, 3)'],
['-100000000000000', 'sigfig(-99999999999999, 5)'],
['-99900000000000', 'sigfig(-99900000000000, 3)'],

['0.300', 'sigfig(0.3, 3)'],
['123.00', 'sigfig(123, 5)'],
['99999.0', 'sigfig(99999, 6)'],
['-0.00123', 'sigfig(-0.00123, 3)'],
['0.00', 'sigfig(0, 3)'],
];
}

Expand Down
26 changes: 26 additions & 0 deletions tests/renderer_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,32 @@ 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.
$q = $this->get_test_formulas_question('testsinglenum');
$q->parts[0]->answer = 'sigfig(3.5, 3)';
$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,50');
}

public function test_render_question_with_separate_unit_field(): void {
$q = $this->get_test_formulas_question('testsinglenumunitsep');
$q->parts[0]->unitpenalty = 0.5;
Expand Down
Loading