(I realize this is an old question, but I'm answering this as I am understanding some things myself...)
My suggestion is that "printf" is converting the float to a double before applying the formatting.
I tried converting the float to a double, and then looking at the double's ToString("f2") result, which it did round in the opposite direction as the float's value was rounded.
Others re-enforce this idea about printf:
C automatically converts the float values to double (it is a standard conversion made when you call a function that takes variable arguments, such as int printf...
https://stackoverflow.com/a/7480244/119418
https://stackoverflow.com/a/6395747/119418
I believe the reason the float rounds up is because 2.995F doesn't convert perfectly into binary and is = 2.99499989F, and we'd expect 2.995 to round up.
I believe the reason the float copied into a double rounds down is because 2.995 as a double <> 2.99499989 as a double, and 2.995 actual double value is greater than that value (more precision closer to its true decimal value), and so that value we'd expect to round down.
It might seem wrong that 2.99499989F rounds up to 3.00 even though it is seemingly less than 2.995, but keep in mind that the 2.99499989 is a decimal, not a float, and you are "converting" it into a float, basically converting base-10 to base-2, and it is really 1's and 0's, then asking it to be rounded in base-10 terms, which means a conversion has to happen. Well, there are at least 2 base-10 values that I mentioned that can convert to that number as a float, and the simplest of those is 2.995.