diff --git a/backup/moodle2/backup_qtype_stack_plugin.class.php b/backup/moodle2/backup_qtype_stack_plugin.class.php
index 210ba97afaad7ccf4484485181ce13958d8f9f06..0d17d559633aeecec893491221fb7c8ae7598c95 100644
--- a/backup/moodle2/backup_qtype_stack_plugin.class.php
+++ b/backup/moodle2/backup_qtype_stack_plugin.class.php
@@ -48,7 +48,7 @@ class backup_qtype_stack_plugin extends backup_qtype_plugin {
                 array('stackversion', 'questionvariables', 'specificfeedback', 'specificfeedbackformat',
                       'questionnote', 'questionsimplify', 'assumepositive', 'assumereal',
                       'prtcorrect', 'prtcorrectformat', 'prtpartiallycorrect', 'prtpartiallycorrectformat',
-                      'prtincorrect', 'prtincorrectformat', 'multiplicationsign', 'sqrtsign',
+                      'prtincorrect', 'prtincorrectformat', 'decimals', 'multiplicationsign', 'sqrtsign',
                       'complexno', 'inversetrig', 'logicsymbol', 'matrixparens', 'variantsselectionseed'));
 
         $stackinputs = new backup_nested_element('stackinputs');
diff --git a/db/install.xml b/db/install.xml
index 399f5ff5fd534bfe6b149f9f57b9cbc79d885c98..33b769b9e6fe22ec4416b77237480b1588424f9f 100644
--- a/db/install.xml
+++ b/db/install.xml
@@ -24,6 +24,7 @@
         <FIELD NAME="prtpartiallycorrectformat" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Text format for the prtpartiallycorrect field."/>
         <FIELD NAME="prtincorrect" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="Standard feedback displayed for any PRT that returns a score of 0."/>
         <FIELD NAME="prtincorrectformat" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Text format for the prtincorrect field."/>
+        <FIELD NAME="decimals" TYPE="char" LENGTH="8" NOTNULL="true" DEFAULT="." SEQUENCE="false" COMMENT="The symbol to use for the decimal separator."/>
         <FIELD NAME="multiplicationsign" TYPE="char" LENGTH="8" NOTNULL="true" DEFAULT="dot" SEQUENCE="false" COMMENT="The symbol to use for multiplication."/>
         <FIELD NAME="sqrtsign" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="1" SEQUENCE="false" COMMENT="Whether to display square roots as surds."/>
         <FIELD NAME="complexno" TYPE="char" LENGTH="8" NOTNULL="true" DEFAULT="i" SEQUENCE="false" COMMENT="How complex numbers should be displayed and represented."/>
diff --git a/db/upgrade.php b/db/upgrade.php
index 154f13b695c8c2f1d84e99d93624f1a669bd8b0a..8337778f080f97eab9b824826d4e925dcde228ac 100644
--- a/db/upgrade.php
+++ b/db/upgrade.php
@@ -933,6 +933,19 @@ function xmldb_qtype_stack_upgrade($oldversion) {
         // Stack savepoint reached.
         upgrade_plugin_savepoint(true, 2023042800, 'qtype', 'stack');
     }
+
+    if ($oldversion < 2023082800) {
+        $table = new xmldb_table('qtype_stack_options');
+        $field = new xmldb_field('decimals', XMLDB_TYPE_CHAR, '8', null, XMLDB_NOTNULL, null, '.');
+
+        // Conditionally launch add field variantsselectionseed.
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // STACK savepoint reached.
+        upgrade_plugin_savepoint(true, 2023082800, 'qtype', 'stack');
+    }
     // Add new upgrade blocks just above here.
 
     // Check the version of the Maxima library code that comes with this version
diff --git a/doc/en/Authoring/Options.md b/doc/en/Authoring/Options.md
index 85c545aecd647f4320b6cd702bcedd5b125b5254..6e0f43506f8eea1dede254e67e6a5df3dbda459a 100644
--- a/doc/en/Authoring/Options.md
+++ b/doc/en/Authoring/Options.md
@@ -36,6 +36,17 @@ any penalty \(\ge 0.66\) and \(\le 0.67\) is changed to \(0.6666667\).
 
 The following options affect how mathematics is displayed.
 
+### Decimal separator ### {#decimals}
+
+Choose the symbol for the decimal separator used by student input.  There are currently two choices.
+
+* `.`, the British decimal point.
+* `,`, the comma, as used in much of Europe.
+
+The design of this option is discussed further in the [developer docs](../Developer/Syntax_numbers.md).
+
+Teachers must always use strict Maxima syntax, which requires `.`, including in test case construction.
+
 ### Multiplication Sign ### {#multiplication}
 
 * (none), e.g. \(x(x+1)\)
diff --git a/doc/en/Authoring/Testing.md b/doc/en/Authoring/Testing.md
index 00a2e6d53fb2c1cb899c0ddb475a09171e89ad54..9c52d0b802d041aca8f827a5bb655792df0c9fe9 100644
--- a/doc/en/Authoring/Testing.md
+++ b/doc/en/Authoring/Testing.md
@@ -56,6 +56,8 @@ Test cases can include a meaningful description of up to 255 characters.  This f
 
 Test cases are always written assuming `simp:false` regardless of the option set elsewhere.  If you want to construct a simplified test case then wrap this in `ev(... , simp)` to simplify the expression generating the test case.  This behaviour is required to enable construction of unsimplified test cases.
 
+Test cases are always written using the period `.` as the decimal separator.  This corresponds to strict Maxima syntax, which teachers should always use.
+
 You can (and should) constuct test cases based on invalid expressions.  If the raw testcase expression cannot be sent to the CAS, e.g. a missing bracket, then this invalidity will be tested.
 
 While test case construction uses `simp:false` Maxima must "evaluate" the expression prior to the result being used by an input as a test case.  This will replace variables by their values.  E.g. the typical case is to define a variable such as `ta` as the teacher's answer in the question variables field and use this throughout the question.  This answer will either be simplified, or not, when the question variables are evaluated.  To construct a test case using the teacher's answer use `ta` as the test case input.
diff --git a/edit_stack_form.php b/edit_stack_form.php
index 18c303cb2ba6a7834330405a5b8ba7af41d476db..ca891a30fbabdc79dbdf2a9821b9e5efd2139826 100644
--- a/edit_stack_form.php
+++ b/edit_stack_form.php
@@ -312,6 +312,11 @@ class qtype_stack_edit_form extends question_edit_form {
         $mform->getElement('prtincorrect')->setValue(array(
                         'text' => $this->stackconfig->prtincorrect));
 
+        $mform->addElement('select', 'decimals',
+            stack_string('decimals'), stack_options::get_decimals_sign_options());
+        $mform->setDefault('decimals', $this->stackconfig->decimals);
+        $mform->addHelpButton('decimals', 'decimals', 'qtype_stack');
+
         $mform->addElement('select', 'multiplicationsign',
                 stack_string('multiplicationsign'), stack_options::get_multiplication_sign_options());
         $mform->setDefault('multiplicationsign', $this->stackconfig->multiplicationsign);
@@ -662,6 +667,7 @@ class qtype_stack_edit_form extends question_edit_form {
                                             $opt->prtpartiallycorrect, $opt->prtpartiallycorrectformat, $question->id);
         $question->prtincorrect          = $this->prepare_text_field('prtincorrect',
                                             $opt->prtincorrect, $opt->prtincorrectformat, $question->id);
+        $question->decimals              = $opt->decimals;
         $question->multiplicationsign    = $opt->multiplicationsign;
         $question->complexno             = $opt->complexno;
         $question->inversetrig           = $opt->inversetrig;
diff --git a/lang/en/qtype_stack.php b/lang/en/qtype_stack.php
index e5bbda358dcc105bf467ae7da28a8436c13acddd..6b802256d20ca9f2cc951a1ac8d21c11eed2badb 100644
--- a/lang/en/qtype_stack.php
+++ b/lang/en/qtype_stack.php
@@ -215,12 +215,15 @@ $string['insertstarsassumesinglechar'] = 'Insert stars assuming single-character
 $string['insertspaces'] = 'Insert stars for spaces only';
 $string['insertstarsspaces'] = 'Insert stars for implied multiplication and for spaces';
 $string['insertstarsspacessinglechar'] = 'Insert stars assuming single-character variables, implied and for spaces';
-$string['multiplicationsign'] = 'Multiplication sign';
-$string['multiplicationsign_help'] = 'Controls how multiplication signs are displayed.';
-$string['multiplicationsign_link'] = '%%WWWROOT%%/question/type/stack/doc/doc.php/Authoring/Options.md#multiplication';
+$string['decimals'] = 'Decimal separator';
+$string['decimals_help'] = 'Choose the symbol, and options, for the decimal separator.';
+$string['decimals_link'] = '%%WWWROOT%%/question/type/stack/doc/doc.php/Authoring/Options.md#decimals';
 $string['multcross'] = 'Cross';
 $string['multdot'] = 'Dot';
 $string['multonlynumbers'] = 'Only numbers';
+$string['multiplicationsign'] = 'Multiplication sign';
+$string['multiplicationsign_help'] = 'Controls how multiplication signs are displayed.';
+$string['multiplicationsign_link'] = '%%WWWROOT%%/question/type/stack/doc/doc.php/Authoring/Options.md#multiplication';
 $string['mustverify'] = 'Student must verify';
 $string['mustverify_help'] = 'Specifies whether the student\'s input is presented back to them as a forced two step process before this input is made available to the scoring mechanism.  Syntax errors are always reported back.';
 $string['mustverify_link'] = '%%WWWROOT%%/question/type/stack/doc/doc.php/Authoring/Inputs.md#Student_must_verify';
diff --git a/questiontype.php b/questiontype.php
index 5ca867b8e1ae521fe24bb6fa8fb94f45b4e5e2e5..cf5d0e18a2c389ba16b2fe7e2e430c6ef28b457d 100644
--- a/questiontype.php
+++ b/questiontype.php
@@ -159,6 +159,7 @@ class qtype_stack extends question_type {
         $options->prtincorrect              = $this->import_or_save_files($fromform->prtincorrect,
                     $context, 'qtype_stack', 'prtincorrect', $fromform->id);
         $options->prtincorrectformat        = $fromform->prtincorrect['format'];
+        $options->decimals                  = $fromform->decimals;
         $options->multiplicationsign        = $fromform->multiplicationsign;
         $options->sqrtsign                  = $fromform->sqrtsign;
         $options->complexno                 = $fromform->complexno;
@@ -471,6 +472,7 @@ class qtype_stack extends question_type {
         }
 
         $question->options = new stack_options();
+        $question->options->set_option('decimals',           $questiondata->options->decimals);
         $question->options->set_option('multiplicationsign', $questiondata->options->multiplicationsign);
         $question->options->set_option('complexno',          $questiondata->options->complexno);
         $question->options->set_option('inversetrig',        $questiondata->options->inversetrig);
@@ -1157,6 +1159,7 @@ class qtype_stack extends question_type {
                         $options->prtpartiallycorrectformat, $contextid, 'prtpartiallycorrect', $questiondata->id);
         $output .= $this->export_xml_text($format, 'prtincorrect', $options->prtincorrect,
                         $options->prtincorrectformat, $contextid, 'prtincorrect', $questiondata->id);
+        $output .= "    <decimals>{$options->decimals}</decimals>\n";
         $output .= "    <multiplicationsign>{$options->multiplicationsign}</multiplicationsign>\n";
         $output .= "    <sqrtsign>{$options->sqrtsign}</sqrtsign>\n";
         $output .= "    <complexno>{$options->complexno}</complexno>\n";
@@ -1298,6 +1301,7 @@ class qtype_stack extends question_type {
         }
         $fromform->prtincorrect          = $this->import_xml_text($xml, 'prtincorrect', $format, $fformat);
         $fromform->penalty               = $format->getpath($xml, array('#', 'penalty', 0, '#'), 0.1);
+        $fromform->decimals              = $format->getpath($xml, array('#', 'decimals', 0, '#'), '.');
         $fromform->multiplicationsign    = $format->getpath($xml, array('#', 'multiplicationsign', 0, '#'), 'dot');
         $fromform->sqrtsign              = $format->getpath($xml, array('#', 'sqrtsign', 0, '#'), 1);
         $fromform->complexno             = $format->getpath($xml, array('#', 'complexno', 0, '#'), 'i');
@@ -1499,6 +1503,7 @@ class qtype_stack extends question_type {
         $fixingdollars = array_key_exists('fixdollars', $fromform);
 
         $this->options = new stack_options();
+        $this->options->set_option('decimals',           $fromform['decimals']);
         $this->options->set_option('multiplicationsign', $fromform['multiplicationsign']);
         $this->options->set_option('complexno',          $fromform['complexno']);
         $this->options->set_option('inversetrig',        $fromform['inversetrig']);
diff --git a/samplequestions/equivalence_reasoning_example.xml b/samplequestions/equivalence_reasoning_example.xml
index 22ed418bc5f912024ca0e1db377a12c4cf36643e..dc913d7d37addb5729b4055389fcb61cf42186d5 100644
--- a/samplequestions/equivalence_reasoning_example.xml
+++ b/samplequestions/equivalence_reasoning_example.xml
@@ -40,6 +40,7 @@
     <prtincorrect format="html">
       <text><![CDATA[<span style="font-size: 1.5em; color:red;"><i class="fa fa-times"></i></span> Incorrect answer.]]></text>
     </prtincorrect>
+    <decimals>.</decimals>
     <multiplicationsign>cross</multiplicationsign>
     <sqrtsign>1</sqrtsign>
     <complexno>i</complexno>
diff --git a/samplequestions/file_download.xml b/samplequestions/file_download.xml
index c5fadb472700c3364adc4108be50ac4284c7ff2d..20887957275ecebfc0ddf127a9e42a1813e6f892 100644
--- a/samplequestions/file_download.xml
+++ b/samplequestions/file_download.xml
@@ -49,6 +49,7 @@ taC: mean(map(third,data));
     <prtincorrect format="html">
       <text><![CDATA[<span style="font-size: 1.5em; color:red;"><i class="fa fa-times"></i></span> Incorrect answer.]]></text>
     </prtincorrect>
+    <decimals>.</decimals>
     <multiplicationsign>dot</multiplicationsign>
     <sqrtsign>1</sqrtsign>
     <complexno>i</complexno>
diff --git a/samplequestions/stack-js/stack-js-issue-specific-example-938.xml b/samplequestions/stack-js/stack-js-issue-specific-example-938.xml
index a56445a430a06cdd858662d99757182f2d8825d6..60906e2891dce32aafb5b36717c928b30e80bef8 100644
--- a/samplequestions/stack-js/stack-js-issue-specific-example-938.xml
+++ b/samplequestions/stack-js/stack-js-issue-specific-example-938.xml
@@ -115,6 +115,7 @@ simp:true;
     <prtincorrect format="html">
       <text></text>
     </prtincorrect>
+    <decimals>.</decimals>
     <multiplicationsign>dot</multiplicationsign>
     <sqrtsign>0</sqrtsign>
     <complexno>i</complexno>
diff --git a/settings.php b/settings.php
index 18c2e342b508ee5a12ed4e0c0632fa965a030189..a30278180e5df06c65c9f3939b5bb0577bc5ad98 100644
--- a/settings.php
+++ b/settings.php
@@ -243,6 +243,11 @@ $settings->add(new admin_setting_configtextarea('qtype_stack/prtincorrect',
         get_string('symbolicprtincorrectfeedback', 'qtype_stack') . ' ' .
             get_string('defaultprtincorrectfeedback', 'qtype_stack'), PARAM_RAW, 60, 3));
 
+$settings->add(new admin_setting_configselect('qtype_stack/decimals',
+        get_string('decimals', 'qtype_stack'),
+        get_string('decimals_help', 'qtype_stack'), '.',
+        stack_options::get_decimals_sign_options()));
+
 $settings->add(new admin_setting_configselect('qtype_stack/multiplicationsign',
         get_string('multiplicationsign', 'qtype_stack'),
         get_string('multiplicationsign_help', 'qtype_stack'), 'dot',
diff --git a/stack/cas/ast.container.class.php b/stack/cas/ast.container.class.php
index 0643507fcaf7a19c16000c04b83e98c9f589331b..b4b0ab3a1627eccc119c52f5e50f0276da37513c 100644
--- a/stack/cas/ast.container.class.php
+++ b/stack/cas/ast.container.class.php
@@ -216,6 +216,9 @@ class stack_ast_container extends stack_ast_container_silent implements cas_late
          * (3) we want ? characters, and no semicolons.
          * (4) we want +- and not #pm#.
          * (5) ntuples have to be stripped off.
+         *
+         * Note we do not construct test cases using the decimal comma.
+         * The only place a comma is accepted is when a student really types it!
          */
 
         $dispval = $this->displayvalue;
diff --git a/stack/maxima/stackmaxima.mac b/stack/maxima/stackmaxima.mac
index ad814be08046981501d7c9792138b4ca27b11365..79e852b1880a0649537bdc192aaaa0a31b670e17 100644
--- a/stack/maxima/stackmaxima.mac
+++ b/stack/maxima/stackmaxima.mac
@@ -3313,4 +3313,4 @@ is_lang(code):=ev(is(%_STACK_LANG=code),simp=true)$
 
 /* Stack expects some output with the version number the output happens at */
 /* maximalocal.mac after additional library loading */
-stackmaximaversion:2023072102$
+stackmaximaversion:2023082800$
diff --git a/stack/options.class.php b/stack/options.class.php
index 3db99be5dd5a5e86ae11148843efb5bfdb9dece8..81eedda364b044fd40a96b6a1e16ffa8ff7edd1e 100644
--- a/stack/options.class.php
+++ b/stack/options.class.php
@@ -37,7 +37,6 @@ class stack_options {
                 'caskey'     => 'OPT_OUTPUT',
                 'castype'    => 'string',
              ),
-            // Currently no way for users to set this option.
             'decimals'       => array(
                 'type'       => 'list',
                 'value'      => '.',
@@ -137,6 +136,7 @@ class stack_options {
     public function set_site_defaults() {
         $stackconfig = stack_utils::get_config();
         // Display option does not match up to $stackconfig->mathsdisplay).
+        $this->set_option('decimals', $stackconfig->decimals);
         $this->set_option('multiplicationsign', $stackconfig->multiplicationsign);
         $this->set_option('complexno', $stackconfig->complexno);
         $this->set_option('inversetrig', $stackconfig->inversetrig);
@@ -254,6 +254,16 @@ class stack_options {
         );
     }
 
+    /**
+     * @return array of choices for the decimal sign select menu.
+     */
+    public static function get_decimals_sign_options() {
+        return array(
+            '.'    => '.',
+            ','    => ',',
+        );
+    }
+
     /**
      * @return array of choices for the multiplication sign select menu.
      */
diff --git a/stack/questiontest.php b/stack/questiontest.php
index 7eaae6db777fbc59ee8c30c9e64d40941cbe6666..6785752ddd731a9f5e9f51987303fade95969c2f 100644
--- a/stack/questiontest.php
+++ b/stack/questiontest.php
@@ -81,6 +81,11 @@ class stack_question_test {
         // Create a completely clean version of the question usage we will use.
         // Evaluated state is stored in question variables etc.
         $question = question_bank::load_question($questionid);
+        // Hard-wire testing to use the decimal point.
+        // Teachers must use strict Maxima syntax, including in test case construction.
+        // I appreciate teachers will, reasonably, want to test the input mechanism but the added internal complexity here is serious.
+        // This complexity includes things like matrix input types which need a valid Maxima expression as the value of the input.
+        $question->options->set_option('decimals', '.');
         if (!is_null($seed)) {
             $question->seed = (int) $seed;
         }
diff --git a/tests/helper.php b/tests/helper.php
index dd31c538d60acba8ed303af210f6c5ad669b74f0..b81839e5255538ffd562f972fd7dc03ecaca3b29 100644
--- a/tests/helper.php
+++ b/tests/helper.php
@@ -302,6 +302,7 @@ class qtype_stack_test_helper extends question_test_helper {
         $formform->prtcorrect = array('text' => 'Correct answer, well done!', 'format' => '1', 'itemid' => 0);
         $formform->prtpartiallycorrect = array('text' => 'Your answer is partially correct!', 'format' => '1', 'itemid' => 0);
         $formform->prtincorrect = array('text' => 'Incorrect answer :-(', 'format' => '1', 'itemid' => 0);
+        $formform->decimals = '.';
         $formform->multiplicationsign = 'dot';
         $formform->sqrtsign = '1';
         $formform->complexno = 'i';
@@ -1966,6 +1967,7 @@ class qtype_stack_test_helper extends question_test_helper {
         $qdata->options->prtpartiallycorrectformat = FORMAT_HTML;
         $qdata->options->prtincorrect              = self::DEFAULT_INCORRECT_FEEDBACK;
         $qdata->options->prtincorrectformat        = FORMAT_HTML;
+        $qdata->options->decimals                  = '.';
         $qdata->options->multiplicationsign        = 'dot';
         $qdata->options->sqrtsign                  = 1;
         $qdata->options->complexno                 = 'i';
@@ -2095,6 +2097,7 @@ class qtype_stack_test_helper extends question_test_helper {
         $qdata->options->prtpartiallycorrectformat = FORMAT_HTML;
         $qdata->options->prtincorrect              = self::DEFAULT_INCORRECT_FEEDBACK;
         $qdata->options->prtincorrectformat        = FORMAT_HTML;
+        $qdata->options->decimals                  = '.';
         $qdata->options->multiplicationsign        = 'dot';
         $qdata->options->sqrtsign                  = 1;
         $qdata->options->complexno                 = 'i';
@@ -2721,6 +2724,7 @@ class qtype_stack_test_helper extends question_test_helper {
                 'text' => 'Incorrect answer :-(',
                 'format' => '1',
                 'itemid' => 56111684);
+        $formform->decimals = '.';
         $formform->multiplicationsign = 'dot';
         $formform->sqrtsign = '1';
         $formform->complexno = 'i';
diff --git a/tests/questiontype_test.php b/tests/questiontype_test.php
index 22e7153ce3786eb01a20cb4c39d173783d7bf9a2..c80ffacaecfa4ef2765d8465ac3b75e3e61ec47c 100644
--- a/tests/questiontype_test.php
+++ b/tests/questiontype_test.php
@@ -218,6 +218,7 @@ class questiontype_test extends qtype_stack_walkthrough_test_base {
     <prtincorrect format="html">
       <text><![CDATA[<p>Incorrect answer.</p>]]></text>
     </prtincorrect>
+    <decimals>.</decimals>
     <multiplicationsign>dot</multiplicationsign>
     <sqrtsign>1</sqrtsign>
     <complexno>i</complexno>
@@ -339,6 +340,7 @@ class questiontype_test extends qtype_stack_walkthrough_test_base {
     <prtincorrect format="html">
       <text><![CDATA[<p>Incorrect answer.</p>]]></text>
     </prtincorrect>
+    <decimals>.</decimals>
     <multiplicationsign>dot</multiplicationsign>
     <sqrtsign>1</sqrtsign>
     <complexno>i</complexno>
@@ -441,6 +443,7 @@ class questiontype_test extends qtype_stack_walkthrough_test_base {
                                                     'format' => FORMAT_HTML, 'files' => array());;
         $expectedq->prtincorrect          = array('text' => '<p>Incorrect answer.</p>',
                                                     'format' => FORMAT_HTML, 'files' => array());;
+        $expectedq->decimals              = '.';
         $expectedq->multiplicationsign    = 'dot';
         $expectedq->sqrtsign              = 1;
         $expectedq->complexno             = 'i';
diff --git a/version.php b/version.php
index 14b84fef3d527908006f77a8d52a54d581c79766..78dc02b714dbb5f8c97dc4bf114a30d774b4f632 100644
--- a/version.php
+++ b/version.php
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2023072102;
+$plugin->version   = 2023082800;
 $plugin->requires  = 2020061500;
 $plugin->cron      = 0;
 $plugin->component = 'qtype_stack';