Совет по квадратичной алгебре для Array, как функция возврата - PullRequest
0 голосов
/ 04 мая 2019

У меня проблема.Я хочу написать метод, который использует формулу PQ для вычисления нулей в квадратичной алгебре.

Как я вижу, C ++ не поддерживает массивы, в отличие от C #, который я использую обычно.Как мне получить либо ноль, либо 1 или 2 результата?Есть ли другой способ без массива, которого не существует?

На самом деле я не нахожу указатели, поэтому мой настоящий код поврежден.

Я был бы рад, если бы кто-нибудь мог мне помочь.

float* calculateZeros(float p, float q)
{
    float *x1, *x2;

    if (((p) / 2)*((p) / 2) - (q) < 0)
        throw std::exception("No Zeros!");

    x1  *= -((p) / 2) + sqrt(static_cast<double>(((p) / 2)*((p) / 2) - (q)));
    x2  *= -((p) / 2) - sqrt(static_cast<double>(((p) / 2)*((p) / 2) - (q)));

    float returnValue[1];
    returnValue[0] = x1;
    returnValue[1] = x2;

    return x1 != x2 ? returnValue[0] : x1;
}

На самом деле этот код не компилируется, но этот код я уже сделал.

1 Ответ

1 голос
/ 13 мая 2019

Есть довольно много проблем с;вначале я отброшу все эти совершенно ненужные скобки, они просто делают код (намного) труднее для чтения:

float* calculateZeros(float p, float q)
{
    float *x1, *x2; // pointers are never initialized!!!

    if ((p / 2)*(p / 2) - q < 0)
        throw std::exception("No Zeros!"); // zeros? q just needs to be large enough!

    x1  *= -(p / 2) + sqrt(static_cast<double>((p / 2)*(p / 2) - q);
    x2  *= -(p / 2) - sqrt(static_cast<double>((p / 2)*(p / 2) - q);
    //  ^ this would multiply the pointer values! but these are not initialized -> UB!!!

    float returnValue[1];
    returnValue[0] = x1; // you are assigning pointer to value here
    returnValue[1] = x2;

    return x1 != x2 ? returnValue[0] : x1;
    //                    ^ value!      ^ pointer!

    // apart from, if you returned a pointer to returnValue array, then you would
    // return a pointer to data with scope local to the function – i. e. the array
    // is destroyed upon leaving the function, thus the pointer returned will get
    // INVALID as soon as the function is exited; using it would again result in UB!
}

Так как ваш код даже не скомпилируется ...

Как я вижу, C ++ не поддерживает массивы

Что ж ... Полагаю, вы имели в виду: 'массивы как возвращаемые значения или параметры функции' ,Это верно для необработанных массивов, они могут быть переданы только как указатели.Но вы можете принять структуры и классы в качестве параметров или использовать их в качестве возвращаемых значений.Вы хотите вернуть оба рассчитанных значения?Таким образом, вы можете использовать, например, std::array<float, 2>;std::array - это оболочка для необработанных массивов, позволяющая избежать всех хлопот, связанных с последним ... Поскольку существует ровно два значения, вы также можете использовать std::pair<float, float> или std::tuple<float, float>.

Хотитебыть в состоянии вернуть 2, 1 или 0 значений?std::vector<float> может быть вашим выбором ...

std::vector<float> calculateZeros(float p, float q)
{
    std::vector<float> results;

    // don't repeat the code all the time...    
    double h = static_cast<double>(p) / 2; // "half"
    s = h * h;                             // "square" (of half)

    if(/* s greater than or equal q */)
    {
        // only enter, if we CAN have a result otherwise, the vector remains empty
        // this is far better behaviour than the exception

        double r = sqrt(s - q); // "root"
        h = -h;

        if(/* r equals 0*/)
        {
            results.push_back(h);
        }
        else
        {
            results.reserve(2); // prevents re-allocations;
                                // admitted, for just two values, we could live with...
            results.push_back(h + r);
            results.push_back(h - r);
        }
    }
    return results;
}

Теперь остается одна заключительная проблема: так как точность даже двойного ограничена, могут возникнуть ошибки округления (и дело даже стоит, если использовать float; Iрекомендовал бы сделать все числами с плавающей запятой одинаковыми, а также параметры и возвращаемые значения!).Вы никогда не должны сравнивать для точного равенства (someValue == 0.0), но рассмотрите некоторые эпсилоны, чтобы покрыть плохо округленные значения:

-epsilon < someValue && someValue < +epsilon

Хорошо, в данном случае, есть два первоначально точных сравнениямы могли бы сделать как можно меньше эпсилон-сравнений.Итак:

double d = r - s;

if(d > -epsilon)
{
    // considered 0 or greater than
    h = -h;
    if(d < +epsilon)
    {
        // considered 0 (and then no need to calculate the root at all...)
        results.push_back(h);
    }
    else
    {
        // considered greater 0
        double r = sqrt(d);
        results.push_back(h - r);
        results.push_back(h + r);
    }
}

Значение эпсилона?Ну, либо используйте исправление, достаточно маленькое значение, либо рассчитайте его динамически на основе меньшего из двух значений (умножьте некоторый небольшой коэффициент на) - и убедитесь, что оно положительное ... Вас может заинтересовать немного больше информация по данному вопросу.Вам не нужно заботиться о том, чтобы не быть C ++ - проблема одинакова для всех языков, использующих представление IEEE754 для двойных чисел.

...