2

So I am trying to write a program which takes a floating point value and represents it in terms of a fraction. I am facing a weird problem. This is my program:

#include<stdio.h>
#include<math.h>
int gcd(int,int);

void main()
{
    int n,n1,n2,g;
    float a,a1;
    printf("Enter number of digits after decimal point: ");
    scanf("%d",&n);
    printf("Enter number: ");
    scanf("%f",&a);
    n2=pow(10,n);
    a1=a*n2;
    n1=(int)a1;
    g=gcd(n1,n2);
    n1/=g;n2/=g;
    printf("The number is %d/%d",n1,n2);
}


int gcd(int a,int b)
{
    int x,flag=0;
    int n1=((a>b)?a:b);
    int n2=((a<b)?a:b);
    for(x=n1;x>=1;x--)
    {
        if(n1%x==0 && n2%x==0)
        {
            flag=1;break;
        }
    }
    if(flag==1)
        return x;
    else
        return 1;

}

Now this program gives the correct answer for numbers with only 1 decimal point. For example:

Enter number of digits after decimal point: 1
Enter number: 2.5
The number is 5/2

However for number with two or more digits after decimal point it gives a wrong answer. For example:

Enter number of digits after decimal point: 2
Enter number: 2.50
The number is 247/99

I know that floating point numbers are not accurate but I did not expect this big a variation. Is there any way to work around this and make this program work???

  • 3
    Floating point numbers are not *always* exact, but 2.5 *is* exact; thus your algorithm is broken. – Antti Haapala -- Слава Україні May 06 '17 at 06:33
  • 1
    `pow` replace with `int pows(int x, int n){ int p = 1; while(n--) p *= x; return p; }` – BLUEPIXY May 06 '17 at 06:37
  • 1
  • @BLUEPIXY that worked..thanks – mayank budhwani May 06 '17 at 06:46
  • 1
    I started writing: _Your GCD algorithm is too fussy. If the values are 'wrong' on the first cycle, the routine processing 'fixes' it. You don't need the conditionals before the loop._ But the body of your loop isn't an implementation of the Euclidean GCD algorithm. Your code executes a lot more cycles in general than Euclid's does. I instrumented your GCD code and the regular GCD code. For `GCD(4795615, 2310) = 55`, your code did 4795561 iterations in the loop, but the regular GCD algorithm did just 2! Euclid was a clever chap. And it is the first documented algorithm — from circa 300 BC. – Jonathan Leffler May 06 '17 at 06:59
  • @JonathanLeffler yeah I know that my code is grossly inefficient as I just used the simplest algorithm I could think of.. I will try to create a program based on the Euclidean GCD algorithm.. Thanks for suggesting.. – mayank budhwani May 06 '17 at 08:15

2 Answers2

5

Works for me. I believe the reason is that you use pow(10, n) and pow is inexact on your platform. Use a simple for-loop instead:

n2 = 1;
for (int c = 0; c < n; c++) {
    n2 *= 10;
}
Community
  • 1
  • 1
  • Thanks..that worked!! Feeling kinda stupid that I did not know this.... – mayank budhwani May 06 '17 at 06:46
  • @Antti Haapala I wonder whether `n1=(int)a1;` may also give wrong value, given that a1 may be stored as 249.999..., resulting in n1 being 249. – X. Liu May 06 '17 at 06:54
  • 3
    @X.Liu: It's tempting to forgo the floating point and read the number as a string. Then count the digits after the decimal point (don't make the user tell you), and remove the decimal point and convert to an appropriately big integer, and do the GCD over 10^n where n is the number of digits after the decimal point. This avoids all floating point inaccuracy issues. – Jonathan Leffler May 06 '17 at 06:57
  • @JonathanLeffler.. That's a really great idea.. Will surely try it out.. thanks.. – mayank budhwani May 06 '17 at 08:16
  • But then again, one thing to note is that `0.09375` is a very exact 3/32..., the number of *decimal* digits doesn't really matter that much – Antti Haapala -- Слава Україні May 06 '17 at 08:23
0

As functions like pow(10, n) may generate results near an integer value instead of an exact integer value. Rather than truncating with (int), use one of the round functions should you want to continue using pow().

Consider long int lround(double x), long int lroundf(float x) which round and covert to an integer type in one call.

The lround and llround functions round their argument to the nearest integer value, rounding halfway cases away from zero, regardless of the current rounding direction. C11 §7.12.9.7 2

n2 = lround(pow(10,n));
a1 = a*n2;
n1 = lroundf(a1);
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256