grade calc: use bigdecimal instead of float for calculations

Convert values to bigdecimal before computing to avoid rounding issues
in certain edge cases.

closes GRADE-1187

Test Plan:
- specs pass

Change-Id: If5140e24c28647350170f559ffe6f231ed1a6a30
Reviewed-on: https://gerrit.instructure.com/151674
Tested-by: Jenkins
Reviewed-by: Keith T. Garner <kgarner@instructure.com>
Reviewed-by: Jeremy Neander <jneander@instructure.com>
Product-Review: Spencer Olson <solson@instructure.com>
QA-Review: Spencer Olson <solson@instructure.com>
This commit is contained in:
Spencer Olson 2018-05-29 09:57:10 -05:00
parent 63990e024c
commit 8bbfbac5d2
2 changed files with 16 additions and 3 deletions

View File

@ -901,7 +901,7 @@ class GradeCalculator
gs[:possible].zero? || gs[:possible].nil?
}
final_grade = relevant_group_sums.reduce(0) { |grade,gs|
grade + (gs[:score].to_f / gs[:possible]) * gs[:weight]
grade + (gs[:score].to_d / gs[:possible]) * gs[:weight].to_d
}
# scale the grade up if total weights don't add up to 100%
@ -912,9 +912,10 @@ class GradeCalculator
final_grade *= 100.0 / full_weight
end
rounded_grade = final_grade&.to_f.try(:round, 2)
{
grade: final_grade.try(:round, 2),
total: final_grade.try(:round, 2),
grade: rounded_grade,
total: rounded_grade,
dropped: dropped
}
else

View File

@ -478,6 +478,18 @@ describe GradeCalculator do
expect(current_groups[@group2.id][:grade]).to eq 100
end
it "calculates the grade without floating point calculation errors" do
@course.update!(group_weighting_scheme: 'percent')
two_groups_two_assignments(50, 200, 50, 100)
@assignment.grade_student(@user, grade: 267.9, grader: @teacher)
@assignment2.grade_student(@user, grade: 53.7, grader: @teacher)
calc = GradeCalculator.new([@user.id], @course.id)
computed_scores = calc.compute_scores.first
# floating point calculation: 66.975 + 26.95 = 93.82499999999999 => 93.82%
# correct calcuation: 66.975 + 26.95 = 93.825 => 93.83%
expect(computed_scores.dig(:current, :grade)).to eq 93.83
end
it "should compute a weighted grade when specified" do
two_groups_two_assignments(50, 10, 50, 40)
expect(@user.enrollments.first.computed_current_score).to eql(nil)