As others have said, this is just a fundamental problem that you get when doing floating-point arithmetic to any base. It just happens that base-2 is the most common one in computers (because it admits efficient hardware implementation).
The best fix, if possible, is to switch to using some kind of quotient representation of the number for your looping, making the floating-point value be derived from that. OK, that sounds overblown! For your specific case, I'd write it as:
int dTimes10 = 20;
double d;
while(dTimes10 != 0) {
dTimes10 -= 2;
d = dTimes10 / 10.0;
}
Here, we're really working with fractions [20/10, 18/10, 16/10, ..., 2/10, 0/10] where the iteration is done with integers (i.e., easy to get correct) in the numerator with a fixed denominator, before converting to floating-point. If you can rewrite your real iterations to work like this, you'll have great success (and they're really not much more expensive than what you were doing before anyway, which is a great trade-off to get correctness).
If you can't do this, you need use equal-within-epsilon as your comparison. Approximately, that's replacing d != target
with abs(d - target) < ε
, where ε (epsilon) selection can sometimes be awkward. Basically, the right value of ε depends on a bunch of factors, but it's probably best selected as 0.001 for the example iteration given the scale of the step value (i.e., it's half a percent of the magnitude of the step, so anything within that is going to be error instead of informative).