The cause of your problem is a fundamental property of floating point types. Have a look here for more information.
In your specific case, the value of 92.345
cannot be represented exactly in a floating point type. A fraction 1/3 cannot be represented in a finite number of decimal (base 10) places (infinitely recurring term). The same effect occurs with floating point, except that floating point values are in binary (base 2). Trying to represent 0.345
in binary (i.e. as a sum of negative powers of two) requires an infinite number of binary digits.
A consequence of this is that the literal 92.345
, when stored in any floating point variable (of type float
, double
, long double
) ete, will have a value that is not exactly equal to 92.345.
The actual value depends on how floating point variables are represented on your system but, presumably, on your system the actual value is slightly less than 92.345.
This will affect your calculations (assuming d
is of a floating point type).
int d1 = (d-scnt)*10;
int d2 = ((d-scnt)*100)-(d1*10);
int d3 = ((d-scnt)*1000) - ((d1*100)+d2*10);
will be subject to rounding errors, for the floating point calculations. The calculation (d-scnt)*10
will produce a value slightly less than 3.45, which means d1
will have a value 3
. Similarly, d2
will get a value of 4
. The problem (in this example) comes in computing d3
, since (d-scnt)*1000
will give a floating point value slightly less than 345, and subtracting ((d1*100)+d2*10)
will give a floating point value that is slightly less than 5
. Conversion to int
rounds towards zero, so d3
will end up with a value of 4
, as you describe.
Incrementing d3
to "fix" the problem is a really bad idea, since floating point rounding can go either way. You will find other values of d
for which rounding cuts the other way (the value as a double
is slightly greater than you expect). There are also values for which your code will produce the behaviour you expect, without modification.
There are various ways to deal with the problem.
One way is to print the value to a string with the required number of digits. This can be done readily using std::ostringstream
(from standard header <sstream>
). This will convert 92.345
to the string "92.345"
. From there, you can extract the digits from the string.
Another way - which I would prefer - is to write your calculations to properly account for floating point rounding. In C++11 and later, you can use round()
from <cmath>
as follows.
int d1 = round((d-scnt)*10);
int d2 = round((d-scnt)*100)-(d1*10);
int d3 = round((d-scnt)*1000) - ((d1*100)+d2*10);
or (to be really explicit about where conversions from double
to int
are occurring)
int d1 = static_cast<int>(round((d-scnt)*10));
int d2 = static_cast<int>(round((d-scnt)*100)) -(d1*10);
int d3 = static_cast<int>(round((d-scnt)*1000)) - ((d1*100)+d2*10);
Before C++11 (which did not supply round()
), the effects of round(x)
can be roughly emulated as floor(x + 0.5)