I found this happened when I was doing string-to-double converting exercise (e.g. atof in stdlib.h). I wanted to put string of “625” (which indicates a fraction part of a double) to a double variable 0.625. The strange thing is when I put it as part of my exercise, it resulted in inaccurate result 0.62500000000000011 or something alike. However, when I put it in a stand alone way, it worked fine, like the following code:
int main(int argc, char **argv) {
string str = "625";
double mask = 0.1;
double frac = 0.0;
for( int i = 0; i < static_cast<int>(str.length()); ++i ) {
frac += (str[i] - '0')*mask;
mask *= 0.1;
}
cout << frac << endl;
}
The code above give the accurate result (0.625). But the following code give the inaccurate result (0.62500000000000011):
string PrintDecimal(string input) {
long int_part = 0;
double frac_part = 0.0;
bool is_positive;
size_t found;
string ret_str;
found = input.find('-');
if( found == string::npos ) {
is_positive = true;
}
else {
is_positive = false;
input.erase(found, 1);
}
found = input.find('.');
if( found == string::npos ) {
int mask = 1;
char app_char;
for( int i = static_cast<int>(input.length()-1); i > -1; --i ) {
int_part += (input[i] - '0')*mask;
mask *= 10;
}
while( int_part != 0 ) {
app_char = (int_part % 2 == 0) ? '0' : '1';
ret_str.push_back(app_char);
int_part /= 2;
}
if( is_positive == false ) {
ret_str.append("-");
}
reverse(ret_str.begin(), ret_str.end());
}
else {
char app_char;
long mask_int = 1;
double mask_frac = 0.1;
string int_part_str = input.substr(0, found);
//string frac_part_str = input.substr(found+1, input.length()-found-1);
string frac_part_str = "0.";
frac_part_str.append(input.substr(found+1, input.length()-found-1));
for( int i = static_cast<int>(int_part_str.length()-1); i > -1; --i ) {
int_part += (int_part_str[i] - '0')*mask_int;
mask_int *= 10;
}
//This converting causes 6*0.1 = 0.6000000000009
/*
for( int i = 0; i < static_cast<int>(frac_part_str.length()); ++i ) {
frac_part += (frac_part_str[i] - '0')*mask_frac;
mask_frac *= 0.1;
}
*/
frac_part = atof(frac_part_str.c_str()); //This works well.
while( int_part != 0 ) {
app_char = (int_part % 2 == 0) ? '0' : '1';
ret_str.push_back(app_char);
int_part /= 2;
}
if( is_positive == false ) {
ret_str.append("-");
}
reverse(ret_str.begin(), ret_str.end());
ret_str.push_back('.');
found = ret_str.find('.');
while( frac_part != 0.0 ) {
if( ret_str.length() - found > 64 ) {
cerr << "Can't express accurately." << endl;
return "Error";
}
frac_part *= 2;
if( frac_part >= 1.0 ) {
ret_str.push_back('1');
frac_part -= 1;
}
else {
ret_str.push_back('0');
}
}
}
cout << ret_str << endl;
return ret_str;
}
The compiler version I used was gcc version 4.2.1 (Apple Inc. build 5666) (dot 3).
Please notice that the commented part in the code, which causes the problem. I ask for your ideas to give a solution to this problem. Thanks!
Well, actually the first isn’t exact either, if we print the result out with more precision we get
and, printing the intermediate results to full precision
To get the most accurate result possible, you have to employ a more complicated algorithm, for example parse the string as a rational number and convert from that.
A quick partial solution is to parse the fractional part to yield
numerator / (10^k),powers of 10 (with nonnegative exponent) can be represented exactly as
doubles for a while (for exponents <= 22, assuming 64-bit IEEE754doubles) and the numerator can be represented exactly too, if the fractional part is not too long. Then you have only one point where an inexact result occurs due to necessary rounding, the final division, and the result is (supposed to be) the closest representable number to the exact mathematical result. (A further point of inexactness is the addition of the fractional part to the integral part.)The above will produce good results for input with not too big integral part and short enough fractional parts, but it will be very wrong for long fractional parts.
The correct parsing and displaying of floating point numbers is a complicated business.