Andrew Binstock adds to his pithy series on quality ratios (unit tests per method, unit test coverage) with a post saying that high-quality code is likely to have around 35% and perhaps even more than 45% of lines devoted to comments.
He also mentions two "commenting" practices that drive me batty -- the boilerplate license agreement header when a URI would do and commented-out source, which is like handing in your homework with crossouts all over the page. (Is a metaphor invoking hand-written homework hopelessly anachronistic?)
I'm somewhat contrarian on the common wisdom regarding comments, an attitude that developed from writing so much code for print publication. Source code was traditionally very difficult to format and inflexible, so when writing code for publication, you use very few comments, explicit-as-possible names, and straightforward-as-possible control structures. Of course you explain the "why" in the article, but you want the "how" to be evident. If a comment is needed within a function written for publication, that's suspicious. But the thing is, that's not a bad attitude to take in the real world! Documentation comments? Absolutely vital. But within a function, I'm skeptical.
The most confusing thing within functions is the combination of state relating to flow-control (if ... else ... if ... case requires a comment saying "Okay, we've figured out that the situation is ... " comment) and the invocation of the consequences ("... So therefore, we know that the correct parameters are ... and we execute the call ... and we store the return in this variable relating to flow-control"). But the solution is not comments, it's refactoring. Create a function that determines the flow-control (or, even better, use a proper object structure with virtual function calls, the result of which is that much flow-control is implicit), and another function that incorporates the call-and-return logic.
Having said that, I don't doubt that the occasional within-function comment can do a world of good, especially in those situations when, due to library constraints, the names of the functions being invoked aren't clearly related to the immediate programming goals.