3

I have a custom code for converting a floating-point number from base 10, to another base which is read in on the standard input. Conversion to another base of the digits before the floating point of the float is implemented by dividing it with the base, and writing the numbers backwards. Conversion of the digits following the floating point is implemented by multiplying them by the base, and writing the digits before the floating point in this temporary result sequentially.

The problem is that I am a victim of the fact that some floating point numbers such as 0.8 in base 10 do not have a finite representation in binary (so they're written as 7.9999...). Hence, for input 2.8, or any input that is larger than 2, and ends in .8, (2.8, 3.8, 4.8) even when I leave the base 10, I get outputs: (2.79999..., 3.79999..., 4.79999...).

The question is: how do I fix this and why don't I get the same mistake for 0.8 or 1.8?

Here is the code:

#include <iostream>

using namespace std;

void printInt(int *a,int k,int base)
{
    if (base<10){
        for (int i=k-1;i>=0;i--)
            cout << a[i];
    }else{
        for (int i=k-1;i>=0;i--)
        {
            if (a[i]<10)
                cout<<a[i];
            else if (a[i]==10)
                cout<<"A";
            else if (a[i]==11)
                cout<<"B";
            else if (a[i]==12)
                cout<<"C";
            else if (a[i]==13)
                cout<<"D";
            else if (a[i]==14)
                cout<<"E";
            else cout<<"F";

        }
    }
}

void printRem(int *a,int k,int base)
{
    if (base<10)
    {
        for(int i=0;i<k;i++){
            cout<<a[i];
        }
    }else{
        for(int i=0;i<k;i++)
        {
            if (a[i]<10)
                cout<<a[i];
            else if (a[i]==10)
                cout<<"A";
            else if (a[i]==11)
                cout<<"B";
            else if (a[i]==12)
                cout<<"C";
            else if (a[i]==13)
                cout<<"D";
            else if (a[i]==14)
                cout<<"E";
            else cout<<"F";

        }
    }
}

int main ()
{
    double n;
    int base;

    cin>>n;
    cin>>base;

    int *whole,*rem,numWhole,numRem;

    whole=NULL;
    numWhole=0;
    rem=NULL;
    numRem=0;

    double floatPart= n - (int)n;
    int intPart=(int)n;

    while (intPart!=0)
    {
        int *temp=new int[numWhole+1];

        int remainder;

        remainder=intPart%base;

        intPart=intPart/base;

        if (whole!=NULL)
            for (int i=0;i<numWhole;i++)
                temp[i]=whole[i];

        delete [] whole;
        temp[numWhole] = remainder;

        whole=temp;
        numWhole++;
    }

    while (floatPart!=0)
    {
        int *temp=new int[numRem+1];

        double nov;
        nov=floatPart*base;

        if (rem!=NULL)
            for (int i=0;i<numRem;i++)
                temp[i]=rem[i];

        temp[numRem]=(int)nov;

        numRem+=1;
        delete [] rem;
        rem=temp;

        floatPart=nov-int(nov);

        if (numRem==10)
            break;
    }

    printInt(whole,numWhole,base);
    if (rem!=NULL) cout<<".";
    printRem(rem,numRem,base);
    cout<<endl;

    cin >> n;

    return 0;
}
repmaj
  • 41
  • 5

2 Answers2

2

why don't I get the same mistake for 0.8 or 1.8?

In those cases, you are still rounding the result when you store to double. All you are observing is that the rounding sometimes gives you the answer you want.

how do I fix this?

In the (strong) likelihood that your double is IEEE-754, it has about 16 digits of accuracy.

If you are content with fewer digits of accuracy, you can round your base 10 result.

When you get any result, such as 2.7999999, round it to fewer digits.

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
  • 2
    I firmly stand by this answer, until convinced otherwise. *(early downvotes)* :) – Drew Dormann Apr 15 '13 at 16:06
  • 1
    What times are “sometimes”? How can you do engineering with “sometimes”? How does a person determine in which direction rounding will occur in a particular circumstances? What base 10 result do you suggest be rounded? The only results are the binary from `cin >> n` and the result produced in base `base`; what base 10 result is there? To how many digits do you suggest it be rounded? Why that number of digits? What are the limits of this rounding, that is, when will it fail to produce the desired result? – Eric Postpischil Apr 15 '13 at 16:43
  • @EricPostpischil The C++ standard does not define the internal format of a `double`. Only that [it won't be exact](http://en.cppreference.com/w/cpp/types/numeric_limits/is_exact). – Drew Dormann Apr 15 '13 at 16:45
  • That does not answer any of the questions. – Eric Postpischil Apr 15 '13 at 16:46
1

As soon as you store the value in a floating point type, you lose the original value. In this case some number (like 0.8 lose the original value by becoming slightly larger while other numbers such as 2.8 lose the original value by becoming smaller.

In any case the normal solution is to either not use floating point numbers (using either fixed point or string representation), or during display to round to a number of digits after the decimal point.

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • But why then does it work for 0.8 and 1.8, and fail for 2.8, 3.8 and so on? – repmaj Apr 15 '13 at 16:03
  • 1
    Because 0.8 is stored in `float` as a number ever so slightly *larger* than 0.8. 1.8 in `float` is a number slightly *larger* than 1.8. Conversely, 2.8 and 3.8 are stored as numbers slightly *smaller* than their base-ten counterparts. Your algorithm essently rounds **down** to the nearest interesting number. – Robᵩ Apr 15 '13 at 16:08
  • 2
    @Robᵩ: No number is stored in a `float` in the code shown. Perhaps you intended `double`. – Eric Postpischil Apr 15 '13 at 16:10