Sometimes it takes clairvoyance to provide the answer a professor is insisting upon
I don't recognize the point he's trying to make about justification of the rounding, except the general concept of math proofs or "showing one's work on paper" in algebra or the calculus.
Anyway, floats have a tendency to loose precision, as you recognize. You can't always determine what the original intent for a particular number was supposed to be.
Take two floats, d1 and d2, set both equal to 10.1.
Now, d3 = d1 + d2;
d3 should be 20.2. The computer "thinks" it's 20.200001.
Now, do the same thing in doubles.
Should be 20.2 again, but the computer "thinks" is 20.199999999999999
Now, given d3 in either case, how could you guess what the number ought to be? You can't.
There is no choice but to round.
This applies to all operations, as you've observed with multiplication. There are values ( be it type double, float - whatever "real" type) which can rather accurately depict the original, but as you've observed this isn't consisitent.
There is no choice but to round, but how can be a valid question. Generally speaking, in order to pin down what the floating point value should "read" or "represent" you must decide on a level of precision. This is applicable beyond programming; virtually all scientific or related industrial calculations are pegged to a tolerance of some limit. It's the specification of that limit that gives you justification for rounding.
Conversion to an integer via multiplication by 100 gives you some authority to round to two decimal places (at least from a non-programming point of view). Lacking decimal places, though, integers don't lend themselves to rounding through addition by 0.5, and as you've observed that addition is, itself, possibly prone to an error.
Now, consider modf, as suggested, with the example values above. Using doubles, the modf result of "d3" is 20.00000, which we'll call d4. To get the fractional part, d5 = d3 - d4, which gives the maddening answer: 0.19999999999999929
Floats fare no better. While d4 provides the reassuring 20.0, d5 returns a frustrating: 0.20000076
Though, you'll recognize, for these particular values, floats would naturally truncate in your integer conversion (without .5 addition) to a "good" result. Other values, however, may not.
At this point I must chuckle at the futility.
BTW, in doubles, d3 = 8888.88 will cause any "print" or "use" of d3 to "think" it's 8888.8799999999992. Cute, eh? That's without ANY math operation upon d3!
In any case, your justification for rounding is clear. The value 0.5 is appropriate as part of a "5/4 rounding method" on paper, but there are exceptions. Consider 8888.845. A float stores this value as 8888.8447, will not round to two decimal places as you expect (it should become 888885 in your integer conversion, if everything were exact). It won't. It will become 888884.
Interestingly, even with a double, the value is represented at 8888.8449999999993, and so
int val = (int)( d3 * 100 +.5) results in 888884.
However, this:
int val = ((int)( d3 * 1000 + 5) / 10;
results in 888885!
So, what's a programmer to do?
Clairvoyance, I'm afraid.