Junilu Lacar wrote:If you must have precision, use BigDecimal
Alas, Junilu, that isn't going to solve sonu's problem. Here's some code that shows why not:
Run this, and you get this output:
From this, you can see a couple of things. First, even the extended precision of a class like BigDecimal doesn't mean you are storing a value like 9.9 exactly. You are simply storing an approximation of 9.9 to quite a number of decimal places. Perhaps accurate enough for things like
testing Ohm's Opinion in your home electronics lab, or landing the Curiousity rover on Mars, but not enough to soothe the tortured soul of some graduate student who placed below a colleague because the colleague falsely scored .0000000000000003552713678800500929355621337890625 higher on some grading scale than he or she did.
Now, there are all kinds of ways to store 9.9
exactly, but even those aren't guaranteed to solve sonu's problem. That's because the problem calls for arbitrary arithmetic on totals, with that arithmetic yielding
exact results. All computer storage is discrete, meaning that all numbers are, one way or another, scaled integers. That's fine for addition, multiplication, and subtraction, which are closed under the whole numbers. But, once you start to divide things into each other, all Heck breaks lose, as the the results may call for "non-terminating decimal expansion," for which, as the exception thrown above rather nihilistically informs us, there is "no exact representable decimal result." So, "precision" isn't the answer. The answer is a representational form that doesn't require division.
For example, suppose the possible grades in a course are "A+," "A", "A-", "B+", and so on, down to "F." One could assign each of these an integer value, such as A+ = 15, A = 14, and so on, down to F = 2. (Why F = 2 instead of F = 3 I leave as an exercise for the new programmer to figure out. Also, why A+ = 15 and F = 2, as opposed to A+ = 1035 and F = 1022 I also leave as an exercise, though I will leave the hint that A+ = 1035 and F = 1022 will work just fine, as will A+ = 5 and F = -8). To get an average grade, one simply adds the integer values of all the course grades (weighted by credits) into a single sum (what totalp does in sonu's code) and all the course credits into a single sum (what totalc does in sonu's code). But
then, one must store these together, never dividing the course-grade sum by the course-credit sum. Instead, one compares the two sums to a scaled range limit expressed as two integers. So, if a student were to take classes of 1, 2, and 3 credits, and score an A-, B, and C in those classes, their overall weighted score would be 13 * 1 + 11 * 2 + 8 * 3 = 59. Their overall credit total would be 1 + 2 + 3 = 6. Their average would then be 59 / 6 = 9.8333... (which you can't represent exactly in a double or even a BigDecimal).
Now, sonu's grading scale is pretty strict: it doesn't round up, so 9.8333... is going to get our student a C+. But, we want to avoid the nasty case where the scale is set exactly at some value that is so close to the student's weighted average that it presents the error sonu found. To do that, we need to represent the scale itself as pairs of integers that
we do not divide.
Happily, we have already done that! Each of our letter grades was set to an integer in the first place. But, how do we compare those integers to the student's weighted total? We do it by scaling the letter-grade numbers up to the total number of credits the student has earned. In our example, that's 6 credits. So, the cut-off value for an A+ is 15 * 6 = 90. Our poor student's 59 isn't very close to that. Sliding down into mediocrity, we find that B- = 10 implies a weighted value of 10 * 6 = 60, and that C+ = 9 implies a weighted value of 9 * 6 = 54. Our student's 59 is between those. Because sonu is a harsh grader, we drop down from 59 to the closest value below that, of 54, and our student gets a "C+," a pat on the back, and a reminder that life holds other rewards beyond merely those which you can obtain in the academy.
Note that this solution can be implemented entirely in native types (integers, that is), and that it is exact, with no rounding or approximations (because it never does any division, nor relies on representations of any non-integer values).
The above is (I think) a complete solution to your problem, which is ordinarily something I believe is frowned upon in the Beginner's forum. However, as you can see, it's not quite as easy as falling off a log, and really requires more of an expanded way of thinking about how a computer works to understand, than just translating it into code. (Also, I remember my days as a graduate student, when we were assigned the problem of writing functions to do basic math on values way too big for storage in 32-bit longs. One of my classmates told the professor he couldn't do it, since his compiler's largest native type was a 32-bit long. The professor leeringly told him, "That's the problem." The poor kid had no clue what to do, and the professor had taught a terrible course up to that point, so I--standing nearby where I could overhear--said, "Try using an array to store your digits." The professor looked at me and shouted, "That is cheating!" Yeah, well... he can sue me. There's a lot more to coding arbitrary-precision math functions than saying, "Try using an array to store your digits," and if the professor hadn't done enough to give his student a chance at solving the problem, that's not my fault. Consider this my final payback to that smug SOB.)
Anyway, the short answer is: think of a way to represent your scores, credits, totals, and grading scales all
strictly in integers.
Good luck!