简体   繁体   中英

Floating point error in C++ code

I am trying to solve a question in which i need to find out the number of possible ways to make a team of two members.(note: a team can have at most two person) After making this code, It works properly but in some test cases it shows floating point error ad i can't find out what it is exactly.

Input: 1st line : Number of test cases 2nd line: number of total person

Thank you

#include<iostream>
using namespace std;
long C(long n, long r)
{
    long f[n + 1];
    f[0] = 1;
    for (long i = 1; i <= n; i++)
    {
        f[i] = i * f[i - 1];
    }
    return f[n] / f[r] / f[n - r];
}

int main()
{
    long n, r, m,t;
    cin>>t;
    while(t--)
    { 
        cin>>n;
        r=1;
        cout<<C(n, min(r, n - r))+1<<endl;
    }
    return 0;
}

You are not using floating-point. And you seem to be using variable sized arrays, which is a C feature and possibly a C++ extension but not standard.

Anyway, you will get overflow and therefore undefined behaviour even for rather small values of n.

In practice the overflow will lead to array elements becoming zero for not much larger values of n.

Your code will then divide by zero and crash.

They also might have a test case like (1000000000, 999999999) which is trivial to solve, but not for your code which I bet will crash.

You aren't getting a floating point exception. You are getting a divide by zero exception. Because your code is attempting to divide by the number 0 (which can't be done on a computer).

When you invoke C(100, 1) the main loop that initializes the f array inside C increases exponentially. Eventually, two values are multiplied such that i * f[i-1] is zero due to overflow. That leads to all the subsequent f[i] values being initialized to zero. And then the division that follows the loop is a division by zero.

Although purists on these forums will say this is undefined, here's what's really happening on most 2's complement architectures. Or at least on my computer....

At i==21 :

f[20] is already equal to 2432902008176640000

21 * 2432902008176640000 overflows for 64-bit signed, and will typically become -4249290049419214848 So at this point, your program is bugged and is now in undefined behavior .

At i==66

f[65] is equal to 0x8000000000000000 . So 66 * f[65] gets calculated as zero for reasons that make sense to me, but should be understood as undefined behavior.

With f[66] assigned to 0, all subsequent assignments of f[i] become zero as well. After the main loop inside C is over, the f[nr] is zero. Hence, divide by zero error.

Update

I went back and reverse engineered your problem. It seems like your C function is just trying to compute this expression:

   N!
 -------------
  R! * (N-R)!

Which is the "number of unique sorted combinations"

In which case instead of computing the large factorial of N!, we can reduce that expression to this:

         n
      [ ∏ i ]
        n-r
 --------------------
        R!

This won't eliminate overflow, but will allow your C function to be able to take on larger values of N and R to compute the number of combinations without error.

But we can also take advantage of simple reduction before trying to do a big long factorial expression

For example, let's say we were trying to compute C(15,5). Mathematically that is:

   15!
 --------
  10! 5!

Or as we expressed above:

 1*2*3*4*5*6*7*8*9*10*11*12*13*14*15
 -----------------------------------   
 1*2*3*4*5*6*7*8*9*10  *  1*2*3*4*5

The first 10 factors of the numerator and denominator cancel each other out:

 11*12*13*14*15
 -----------------------------------   
 1*2*3*4*5

But intuitively, you can see that "12" in the numerator is already evenly divisible by denominators 2 and 3. And that 15 in the numerator is evenly divisible by 5 in the denominator. So simple reduction can be applied:

 11*2*13*14*3
 -----------------------------------   
 1  * 4

There's even more room for greatest common divisor reduction, but this is a great start.

Let's start with a helper function that computes the product of all the values in a list.

long long multiply_vector(std::vector<int>& values)
{
    long long result = 1;
    for (long i : values)
    {
        result = result * i;
        if (result < 0)
        {
            std::cout << "ERROR - multiply_range hit overflow" << std::endl;
            return 0;
        }
    }
    return result;
}

Not let's implement C as using the above function after doing the reduction operation

long long C(int n, int r)
{
    if ((r >= n) || (n < 0) || (r < 0))
    {
        std::cout << "invalid parameters passed to C" << std::endl;
        return 0;
    }

    // compute
    //    n!
    //  -------------
    //   r! *  (n-r)!
    // 
    // assume (r < n)

    // Which maps to

    //      n
    //    [∏ i]
    //    n - r
    // --------------------
    //     R!


    int end = n;
    int start = n - r + 1;

    std::vector<int> numerators;
    std::vector<int> denominators;
    long long numerator = 1;
    long long denominator = 1;

    for (int i = start; i <= end; i++)
    {
        numerators.push_back(i);
    }
    for (int i = 2; i <= r; i++)
    {
        denominators.push_back(i);
    }

    size_t n_length = numerators.size();
    size_t d_length = denominators.size();
    for (size_t n = 0; n < n_length; n++)
    {
        int nval = numerators[n];
        for (size_t d = 0; d < d_length; d++)
        {
            int dval = denominators[d];

            if ((nval % dval) == 0)
            {
                denominators[d] = 1;
                numerators[n] = nval / dval;
            }
        }
    }

    numerator = multiply_vector(numerators);
    denominator = multiply_vector(denominators);
    if ((numerator == 0) || (denominator == 0))
    {
        std::cout << "Giving up.  Can't resolve overflow" << std::endl;
        return 0;
    }

    long long result = numerator / denominator;

    return result;
}

Array syntax as:

type name[size]

Note: size must a constant not a variable

Example #1:

int name[10];

Example #2:

const int asize = 10;
int name[asize];

You don't specify what you mean by "floating point error" - I reckon you are referring to the fact that you are doing an integer division rather than a floating point one so that you will always get integers rather than floats.

int a, b; 
a = 7; 
b = 2; 
std::cout << a / b << std::endl;

this will result in 3, not 3.5! If you want floating point result you should use floats instead like this:

float a, b; 
a = 7; 
b = 2; 
std::cout << a / b << std::end;

So the solution to your problem would simply be to use float instead of long long int .

Note also that you are using variable sized arrays which won't work in C++ - why not use std::vector instead??

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM