简体   繁体   English

C ++代码中的浮点错误

[英]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. 而且您似乎正在使用可变大小的数组,这是C的功能,可能是C ++的扩展,但不是标准的。

Anyway, you will get overflow and therefore undefined behaviour even for rather small values of n. 无论如何,即使n的值很小,也将发生溢出,从而导致不确定的行为。

In practice the overflow will lead to array elements becoming zero for not much larger values of n. 实际上,溢出将导致数组元素在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. 他们也可能有一个测试用例,例如(1000000000,999999999),这很容易解决,但对于我敢打赌的您的代码而言,它不会崩溃。

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). 因为您的代码正在尝试除以数字0(在计算机上无法完成)。

When you invoke C(100, 1) the main loop that initializes the f array inside C increases exponentially. 当您调用C(100, 1) ,初始化Cf数组的主循环呈指数增长。 Eventually, two values are multiplied such that i * f[i-1] is zero due to overflow. 最终,两个值相乘,使得i * f[i-1]由于溢出而为零。 That leads to all the subsequent f[i] values being initialized to zero. 这导致所有后续的f [i]值初始化为零。 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. 尽管这些论坛上的纯粹主义者会说这是不确定的,但这是大多数2的补码体系结构上真正发生的事情。 Or at least on my computer.... 或至少在我的电脑上...

At i==21 : i==21

f[20] is already equal to 2432902008176640000 f[20]已经等于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 . 21 * 2432902008176640000对于64位带符号签名会溢出,并且通常会变为-4249290049419214848因此,此时,您的程序存在错误,并且现在处于未定义的行为中

At i==66 i==66

f[65] is equal to 0x8000000000000000 . f[65]等于0x8000000000000000 So 66 * f[65] gets calculated as zero for reasons that make sense to me, but should be understood as undefined behavior. 因此66 * f[65]出于对我有意义的原因而被计算为零,但应理解为未定义的行为。

With f[66] assigned to 0, all subsequent assignments of f[i] become zero as well. f[66]分配为0, f[i]所有后续分配也将变为零。 After the main loop inside C is over, the f[nr] is zero. C内部的主循环结束之后, f[nr]为零。 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: 看来您的C函数正在尝试计算此表达式:

   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. 这不会消除溢出,但是将使您的C函数能够采用较大的N和R值来正确计算组合数。

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). 例如,假设我们正在尝试计算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: 分子和分母的前10个因子相互抵消:

 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. 但从直观上看,您可以看到分子中的“ 12”已被分母2和3均分。并且分子中的15被分母5均分了。 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 在进行归约运算后,不要使用上述功能来实现C

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: 范例1:

int name[10];

Example #2: 范例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! 这将导致3,而不是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 . 因此,解决问题的方法只是使用float而不是long long int

Note also that you are using variable sized arrays which won't work in C++ - why not use std::vector instead?? 另请注意,您正在使用在C ++中不起作用的可变大小数组-为什么不使用std::vector

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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