diff --git a/core/migrations/0013_auto_20190308_1448.py b/core/migrations/0013_auto_20190308_1448.py
new file mode 100644
index 0000000000000000000000000000000000000000..3f2e76d83b47225f972a0469cdbd1062513fb200
--- /dev/null
+++ b/core/migrations/0013_auto_20190308_1448.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.1.4 on 2019-03-08 14:48
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0012_auto_20190306_1611'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='feedback',
+            name='score',
+            field=models.DecimalField(decimal_places=2, default=0, max_digits=5),
+        ),
+    ]
diff --git a/core/models.py b/core/models.py
index 224eca210c4242137dbbd5f905a7a336733bcfe2..77ae5c3f3a3e34905609aa1e8ffb697be2be5811 100644
--- a/core/models.py
+++ b/core/models.py
@@ -447,7 +447,7 @@ class Feedback(models.Model):
     origin : IntegerField
         Of whom was this feedback originally created. She below for the choices
     """
-    score = models.PositiveIntegerField(default=0)
+    score = models.DecimalField(max_digits=5, decimal_places=2, default=0)
     created = models.DateTimeField(auto_now_add=True)
     is_final = models.BooleanField(default=False)
 
@@ -456,6 +456,9 @@ class Feedback(models.Model):
         on_delete=models.CASCADE,
         related_name='feedback')
 
+    # the denominators that are allowed for the decimal score interpreted as a fraction
+    ALLOWED_DENOMINATORS = [1, 2]
+
     # how was this feedback created
     (
         WAS_EMPTY,
diff --git a/core/serializers/feedback.py b/core/serializers/feedback.py
index 94949fbb48186c97d63f69151c5ba96dbe2ddc8e..808fa07d1892304e136913daec1bc40ffebd3e6f 100644
--- a/core/serializers/feedback.py
+++ b/core/serializers/feedback.py
@@ -163,6 +163,12 @@ class FeedbackSerializer(DynamicFieldsModelSerializer):
             raise serializers.ValidationError(
                 f'Score has to be in range [0..{submission.type.full_score}].')
 
+        if score.as_integer_ratio()[1] not in Feedback.ALLOWED_DENOMINATORS:
+            raise serializers.ValidationError(
+                f'For fractional scores, the denominator must be one of '
+                f'{Feedback.ALLOWED_DENOMINATORS}'
+            )
+
         has_full_score = score == submission.type.full_score
         has_feedback_lines = ('feedback_lines' in data and
                               len(data['feedback_lines']) > 0 or
diff --git a/core/tests/test_feedback.py b/core/tests/test_feedback.py
index d82dcca27e1c6121d4f7fd28ceb5f47fc611d119..ad541d8f7f173a8171f9637b2a3d2dab136e5c92 100644
--- a/core/tests/test_feedback.py
+++ b/core/tests/test_feedback.py
@@ -115,6 +115,7 @@ class FeedbackCreateTestCase(APITestCase):
                                             text=text)
 
     def setUp(self):
+        self.sub.refresh_from_db()
         self.client.force_authenticate(user=self.tutor)
         self.subscription = models.SubmissionSubscription.objects.create(
             owner=self.tutor,
@@ -179,6 +180,32 @@ class FeedbackCreateTestCase(APITestCase):
         self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
         self.assertEqual(Feedback.objects.count(), 0)
 
+    def test_cannot_create_feedback_with_score_with_invalid_fractional_denominator(self):
+        data = {
+            'score': 1.500000001,
+            'is_final': False,
+            'of_submission': self.assignment.submission.pk
+        }
+        self.assertEqual(Feedback.objects.count(), 0)
+        response = self.client.post(self.url, data, format='json')
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        self.assertEqual(Feedback.objects.count(), 0)
+
+    def test_can_create_feedback_with_half_points(self):
+        data = {
+            'score': 0.5,
+            'is_final': False,
+            'of_submission': self.assignment.submission.pk,
+            'feedback_lines': {
+                '2': {
+                    'text': 'Why you no learn how to code, man?'
+                }
+            }
+        }
+        self.client.post(self.url, data, format='json')
+        object_score = self.sub.feedback.score
+        self.assertEqual(object_score, 0.5)
+
     def test_check_score_is_set_accordingly(self):
         data = {
             'score': 5,
@@ -200,7 +227,7 @@ class FeedbackCreateTestCase(APITestCase):
             'is_final': False,
             'of_submission': self.assignment.submission.pk,
             'feedback_lines': {
-                '4': {
+                '3': {
                     'text': 'Nice meth!'
                 }
             }
diff --git a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue
index 891c567b15c8ce9c5d9a0e38c8520f23fe2a23e4..2987961a12e905d4f978606476629dd6424c378b 100644
--- a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue
+++ b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue
@@ -12,32 +12,29 @@
           <span>Skip this submission</span>
         </v-tooltip>
         <v-spacer />
-        <v-alert class="score-alert ma-3"
-        color="error"
-        icon="warning"
-        :value="scoreError">{{ scoreError }}</v-alert>
       </v-flex>
       <v-flex>
         <v-layout wrap class="score-submit-container">
           <v-flex xs5 class="score-flex">
             <span class="mr-2">Score:</span>
-            <input class="score-text-field" 
+            <input class="score-text-field"
             id="score-input"
-            type="number" 
-            v-model="score" 
-            @input="validateScore" 
+            type="number"
+            step="0.5"
+            v-model="score"
+            @input="validateScore"
             @change="validateScore" />
             <span>&nbsp;/ {{fullScore}}</span>
-            <v-btn outline round flat 
+            <v-btn outline round flat
             id="score-zero"
-            class="score-button" 
-            @click="score=0" 
+            class="score-button"
+            @click="score=0"
             color="red lighten-1">0
             </v-btn>
-            <v-btn outline round flat 
+            <v-btn outline round flat
             id="score-full"
-            @click="score=fullScore" 
-            color="blue darken-3" 
+            @click="score=fullScore"
+            color="blue darken-3"
             class="score-button">{{fullScore}}
             </v-btn>
           </v-flex>
@@ -45,11 +42,11 @@
             <v-layout>
               <v-flex xs4>
                 <v-tooltip top v-if="showFinalCheckbox">
-                  <v-toolbar-items class="final-container" 
+                  <v-toolbar-items class="final-container"
                   slot="activator">
                     <label>Final</label>
-                    <v-checkbox slot="activator" 
-                    v-model="isFinal" 
+                    <v-checkbox slot="activator"
+                    v-model="isFinal"
                     class="final-checkbox" />
                   </v-toolbar-items>
                   <span>Non final feedback will be sent to the reviewer.</span>
@@ -57,10 +54,10 @@
               </v-flex>
               <v-flex xs>
                 <v-tooltip top>
-                  <v-btn color="success" 
+                  <v-btn color="success"
                   id="submit-feedback"
-                  slot="activator" 
-                  :loading="loading" 
+                  slot="activator"
+                  :loading="loading"
                   @click="submit">Submit
                     <v-icon>chevron_right</v-icon>
                   </v-btn>
@@ -71,6 +68,12 @@
           </v-flex>
         </v-layout>
       </v-flex>
+      <v-flex v-if="scoreError">
+        <v-alert class="score-alert ma-3"
+        color="error"
+        icon="warning"
+        :value="scoreError">{{ scoreError }}</v-alert>
+      </v-flex>
     </v-layout>
   </v-container>
 </template>
diff --git a/grady/settings/default.py b/grady/settings/default.py
index a147a3d85ea8b09bfbffc01eb2e9cde76fa84e1f..36cca69c88ad14a070432e9dc835cde0d64b1fe8 100644
--- a/grady/settings/default.py
+++ b/grady/settings/default.py
@@ -150,6 +150,7 @@ REST_FRAMEWORK = {
     'DEFAULT_PARSER_CLASSES': (
         'djangorestframework_camel_case.parser.CamelCaseJSONParser',
     ),
+    'COERCE_DECIMAL_TO_STRING': False,
 }
 
 JSON_CAMEL_CASE = {