I graduated in 1990 in Electrical Engineering and since then I have been in university, doing research in the field of DSP. To me programming is more a tool than a job.
I guess that it is necessary some context, but to make it short:
High-level comments explaining what (nothow) a specific function/package/class does is useful since allows the reader to orient itself in the code. Also, that is the kind of information that does not change frequently, so the probability that the comment gets stale is minimal
Comments to explain a non-trivial algorithm (e.g., the Euclidean GCD) can be useful, also to keep track of the conditions that are true at every point. Here assertions, loop invariant and similar stuff are your friend since they allow you to document what is going on in a way that the compiler can check, minimizing the probability of getting stale
Comments that explain why something is done. This is quite uncommon, in my experience, but it happens every now and then. Maybe there is a counter-intuitive way to do the things or maybe there is a strange check that seems useless and you would be tempted to "optimize it away," but it actually takes care of some borderline case that it is not easy to imagine.
The kind of comments that are wrong are maybe the most common ones that try to clarify what would had been clear if the code were written nicely. For example, instead of writing this
intd;intc;/* total cost */inte[7];/* daily expense */for(d=0;d<7;d++)// Loop over the week{c=c+e[d];}
I guess that it is necessary some context, but to make it short:
better this