-5

I have a problem. I want to write a method, which uses the PQ-Formula to calculate Zeros on quadratic algebra.

As I see C++ doesn't support Arrays, unlike C#, which I use normally. How do I get either, ZERO, 1 or 2 results returned? Is there any other way without Array, which doesn't exists?

Actually I am not into pointers so my actual code is corrupted.

I'd glad if someone can help me.

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;
}

Actualy this code is not compilable but this is the code I've done so far.

Liwu
  • 14
  • 1
  • 6
  • 6
    _"As I see C++ doesn't support Arrays ..."_ [Huh??](https://en.cppreference.com/w/cpp/container) – πάντα ῥεῖ May 04 '19 at 18:30
  • 2
    "As I see C++ doesn't support Arrays" - Bollocks. We have `std::array` for static arrays and `std::vector` for dynamic arrays. And there's also the old C-style arrays (but please don't use those). – Jesper Juhl May 04 '19 at 18:30
  • May I suggest not to throw an exception in case of negative delta? Having zero real solutions is one of the possible *expected* outcome. – Bob__ May 04 '19 at 18:35
  • `float *x1, *x2;` -- Why are these pointers? I guess this is what happens when you try to translate from your favorite language to C++, line-by-line, without learning C++. You totally get wrong the basics of the C++ language. – PaulMcKenzie May 04 '19 at 18:40
  • Ah I didn't know C++ has Array. I will take a look. thank you, I will comeback if I have done it. – Liwu May 04 '19 at 18:41
  • Please note that `*=` doesn't do what it seems you think it does. – Bob__ May 04 '19 at 18:42
  • std::array calculateZeros(float p, float q) { if ((((p) / 2)*((p) / 2) - (q)) < 0) throw std::exception("No Zeros!"); std::array arr = { -((p) / 2) + sqrt(static_cast(((p) / 2)*((p) / 2) - (q))), -((p) / 2) - sqrt(static_cast(((p) / 2)*((p) / 2) - (q))) }; return arr; } Okay I have done it. Thanks. – Liwu May 04 '19 at 18:46
  • Don't put code in comments. Edit your post. Look at your comment, the code in the comment is very difficult to read. – Thomas Matthews May 04 '19 at 19:13
  • And drop those parentheses around the parameters. `p` and `q` are not macros... Apart from, if they were, then the macros themselves should already contain the parantheses... – Aconcagua May 13 '19 at 07:39

1 Answers1

1

There are quite a fiew issues with; at very first, I'll be dropping all those totally needless parentheses, they just make the code (much) harder to read:

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!
}

As is, your code wouldn't even compile...

As I see C++ doesn't support arrays

Well... I assume you meant: 'arrays as return values or function parameters'. That's true for raw arrays, these can only be passed as pointers. But you can accept structs and classes as parameters or use them as return values. You want to return both calculated values? So you could use e. g. std::array<float, 2>; std::array is a wrapper around raw arrays avoiding all the hassle you have with the latter... As there are exactly two values, you could use std::pair<float, float>, too, or std::tuple<float, float>.

Want to be able to return either 2, 1 or 0 values? std::vector<float> might be your choice...

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;
}

Now there's one final issue left: as precision even of double is limited, rounding errors can occur (and the matter is even worth if using float; I would recommend making all floats to doubles, parameters and return values as well!). You shouldn't ever compare for exact equality (someValue == 0.0), but consider some epsilon to cover badly rounded values:

-epsilon < someValue && someValue < +epsilon

Ok, in given case, there are two originally exact comparisons involved, we might want to do as little epsilon-comparisons as possible. So:

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);
    }
}

Value of epsilon? Well, either use a fix, small enough value or calculate it dynamically based on the smaller of the two values (multiply some small factor to) – and be sure to have it positive... You might be interested in a bit more of information on the matter. You don't have to care about not being C++ – the issue is the same for all languages using IEEE754 representation for doubles.

Aconcagua
  • 24,880
  • 4
  • 34
  • 59