0

I'm trying to solve this exercise :

Converts floating point number into decimal. e.g if input is 12.345 then output should be 12345

... so this is my solution :

double d=0;
cout<<"Enter a double : ";
cin>>d;

while(d-(int)d > 0.)
    d*=10;

cout<<"Result : "<<d<<endl;

I think that algorithm is correct theoretically, but practically some values not work for me !!

for example :

1.123 work correctly and the program gives 1123,

but 1.12 doesn't work and gives infinite loop !!

where is the problem and what is the solution to make my program work correctly ?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Az.Youness
  • 2,167
  • 1
  • 24
  • 33
  • 2
    What is the "0." in the while loop condition ? I mean why the "." ? – Rndm Sep 21 '13 at 14:29
  • Inexact representation of doubles is your problem. Test for the absolute value of the difference to be less than some small number instead. – Floris Sep 21 '13 at 14:31
  • 1
    @Rndm: `0` by itself is an `int`. `0.` is the same as `0.0`, and is a constant of type `double` (not `int`). – abelenky Sep 21 '13 at 14:31
  • 1
    Binary floating point numbers can't represent all (or even most) decimal numbers exactly. `12.345` might in fact be represented in memory as something like `123.4999...` (and rounded when you print it out), leading to an infinite loop. – millimoose Sep 21 '13 at 14:31
  • 1
    Honestly the easier thing here would be to `sprintf()` the `double`, remove the decimal point, and parse that into an `int` again. I.e. rely on the standard library to handle these things for you. – millimoose Sep 21 '13 at 14:32
  • 3
    How many duplicates...and who can quote a URL for "What every computer scientist should know about floating point arithmetic"? – Jonathan Leffler Sep 21 '13 at 14:33
  • 1
    I think @millimoose has a point. But read the input as a string. And trim trailing zeros first. – Floris Sep 21 '13 at 14:35
  • The user will sure find useful the canonical [What Every Computer Scientist Should Know About Floating Point Arithmetic](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html), but I actually think that for this particular (pointless?) problem, the comment by millimose is a good idea, because it's also possible to choose the precision for the representation by just changing a format string :) – Alberto Moriconi Sep 21 '13 at 14:39
  • That's potentially a very hard problem, depending on how deep you want to go. Here's a paper that gives an algorithm, http://www.cs.indiana.edu/~burger/FP-Printing-PLDI96.pdf – john Sep 21 '13 at 14:43

5 Answers5

3

The problem is the rounding involved in floating point arithmetic: You cannot represent all the number which you can represent in a decimal number in binary.

As an example, imagine you ran your algorithm with a factor of 3 on 1/3. 1/3, in decimal, is represented by 0.3333, so your algorithm would calculate the following:

 0.3333 * 3 =  0.9999
 0.9999 * 3 =  2.9997
 2.9997 * 3 =  8.9991
 8.9991 * 3 = 26.9973
26.9993 * 3 = 80.9919

As you can see, the fractional part does not disappear as you expected.

Like 1/3 is not representable in decimal, 1/10 is not representable in binary, so you cannot expect 10*(1/10) to evaluate to 1 in binary, just as you cannot expect 3*(1/3) to evaluate to 1 in decimal.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
2

Floating point rounding errors — because binary floating point variables cannot represent all decimal values exactly. Some values that are exact when printed as decimal numbers are stored in a binary number that is marginally smaller, so the computation in the while condition leaves you with a negative number.

To debug: print the values — probably with precision control. That's easier (more succinct) using printf() et al than cout et al.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
2

The following solution was inspired by the comment that Millimoose made - so thanks!

Read the input as a string. Strip any trailing zeros to the right of the decimal. Remove the decimal. Convert to int.

Note - this works particularly well because it circumvents the problem of finite precision in the representation of a decimal - a problem you will always encounter because you start with a floating point number that is human-readable. However, if you need a function that is passed an inexact representation of a floating point number, the best you can hope for is to use your method, and stop the loop after a finite number of iterations.

Even better would be to subtract, and multiply the remainder by 10. In that way your loop can always terminate (this is how number to string representation functions work). If you intend to convert to an integer at the end you need to make sure you don't get overflow. If you collect the numbers into another string you don't have to worry about overflow.

In short - as stated the problem does not have an exact solution, but the above should give you some insights into approaches to take.

EDIT here is some code that does what I described (different approaches - and demonstrating the overflow problem):

#include <iostream>
#include <math.h>

int main(void){
  double d=0;
  char buffer[100];
  char buf2[100];
  long int i,j;

  std::cout<<"Enter a double : ";
  fgets(buffer, 100, stdin);
  strncpy(buf2, buffer, 100);

  // method 1: use string manipulation to get the answer
  i = (int)(strstr(buffer, ".") - buffer);
  if (i >= 0)
  {
    j = strlen(buffer)-1;
    while(buffer[j-1]=='0')
    {
      j--;
    }
    for(; i<j; i++)
    {
      buffer[i]=buffer[i+1];
    }
  buffer[j-1]='\0'; // trim it
  }
  std::cout << "The integer representation of that string is " << buffer << "\n";

  // method 2: use a finite length conversion
  sscanf(buf2, "%lf", &d);
  printf("The string converted to double is %lf\n", d);
  j = (long int)d;
  sprintf(buffer, "%ld", j);
  int k;
  k = strlen(buffer);
  d -= j;
  for(i=0; i<20; i++)
  {
    d *= 10;
    if (fabs(d) > 0)
    {
      sprintf(buffer + k, "%01d", (int)d);
      k++;
      printf("i = %ld; j is now %ld; d is %lf\n", i, j, d);
      j = 10 * j + (int) d;
      d -= (int) d;
    }
    else break;
  }
  printf("after conversion, the number is %ld\n", j);
  printf("using the string method, it is %s\n", buffer);
}

Here is sample output:

Enter a double : 123.456001
The integer representation of that string is 123456001
The string converted to double is 123.456001
i = 0; j is now 123; d is 4.560010
i = 1; j is now 1234; d is 5.600100
i = 2; j is now 12345; d is 6.001000
i = 3; j is now 123456; d is 0.010000
i = 4; j is now 1234560; d is 0.100000
i = 5; j is now 12345600; d is 1.000000
i = 6; j is now 123456001; d is 0.000000
i = 7; j is now 1234560010; d is 0.000000
i = 8; j is now 12345600100; d is 0.000001
i = 9; j is now 123456001000; d is 0.000005
i = 10; j is now 1234560010000; d is 0.000054
i = 11; j is now 12345600100000; d is 0.000545
i = 12; j is now 123456001000000; d is 0.005448
i = 13; j is now 1234560010000000; d is 0.054479
i = 14; j is now 12345600100000000; d is 0.544787
i = 15; j is now 123456001000000000; d is 5.447873
i = 16; j is now 1234560010000000005; d is 4.478733
i = 17; j is now -6101143973709551562; d is 4.787326
i = 18; j is now -5671207515966860768; d is 7.873264
i = 19; j is now -1371842938539952825; d is 8.732636
after conversion, the number is 4728314688310023374
using the string method, it is 12345600100000000054478

As you can see from this, there were some "rounding error bits" stuck way to the right; as I was multiplying and subtracting, these bits eventually became visible. This loop could go on forever. I stopped it at 20, which was actually too many - j had already overflowed by this time. I think it shows you what's going on exactly, though.

You have to define the precision with which you want your answer, or you cannot solve this problem in general terms. If you truly need more than 10 or so digits I recommend that you use a string based approach, or things like BigDecimal.

PS I apologize for mixing C and C++. I never was much of a C++ person, so this comes more naturally on a Saturday morning.

PS2: Modifying the above code a little bit, I could find the full decimal representation of the two numbers you mentioned. It turns out that

1.12 --> 112000000000000010658141036401502788066864013671875

In other words, the actual representation is slightly larger than 1.12. On the other hand,

1.123 --> 11229999999999999982236431605997495353221893310546875

As you can see, it is slightly smaller. This is why your loop never terminates, since you are checking whether d - (int) d > 0.0. As the int conversion rounds down, this condition is always true.

Floris
  • 45,857
  • 6
  • 70
  • 122
1

You are dealing with floating point numbers so always remember you have to be careful about precision problems!

Generally speaking this is impossible if you read this value to double variable because when you input 3.33 it is represented as something like 3.32999978434 and many more signs. Do you really want to convert it to integer this way?

sasha.sochka
  • 14,395
  • 10
  • 44
  • 68
-3

Why don't you print out the values and figure it out for yourself?

double d=0;
cout<<"Enter a double : ";
cin>>d;

while(d-(int)d > 0.){
    cout << "d - (int)d is: " << d << " - " << (int)d <<endl;
    d*=10;
    cout << "Now d is: " << d << endl;
}

cout<<"Result : "<<d<<endl;
abelenky
  • 63,815
  • 23
  • 109
  • 159