4

I am trying to write a program to solve a quadratic equation whose coefficients' values do not exceed 100 by absolute value and it is guaranteed that if any roots exist, they are integers. Here's what I've tried:

#include <cmath>
#include <iostream>

int main() {
  int a, b, c;  // coefficients of quadratic equation
  std::cin >> a >> b >> c;
  int d = b * b - 4 * a * c;  // discriminant

  if (d < 0)
    std::cout << "No roots";
  else if (d == 0)
    std::cout << "One root: " << -b / (2 * a);
  else
    std::cout << "Two roots: " << (-b - std::sqrt(d)) / (2 * a) << " "
              << (-b + std::sqrt(d)) / (2 * a);

  return 0;
}

It works fine, however, Visual Studio 2019 shows this warning:

Arithmetic overflow: Using operator '*' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator '*' to avoid overflow (io.2).

enter image description here

Exactly why does this warning pop up and what is it trying to tell me? What am I doing wrong and how can I fix the problem?

I've seen this on SO, but I don't believe it's a bug.

  • Why do you think this is different from the linked question? It seems to me to be the exact same situation. – François Andrieux Dec 12 '19 at 16:04
  • 1
    I don't like the word "casting", as there isn't any. One would expect compiler creators to know the difference between implicit and explicit conversion. – molbdnilo Dec 12 '19 at 16:09
  • *and it is guaranteed that if any roots exist, they are integers.* -- You may [want to read this](https://stackoverflow.com/questions/43655668/are-all-integer-values-perfectly-represented-as-doubles). – PaulMcKenzie Dec 12 '19 at 16:11
  • 3
    It's not actually a bug. It's saying that `2 * a` results in a 32-bit value, which, depending on the value of `a`, might overflow. Since the result gets converted into a 64-bit value (for the division), it is suggesting you convert prior to the multiplication. You can fix it by doing `2.0 * a` or `2LL * a` – ChrisMM Dec 12 '19 at 16:12
  • 1
    _"I don't believe it's a bug."_ Why not? If you have a criticism of a question or answer on that page you should give it on that page rather than re-asking the same question. – Lightness Races in Orbit Dec 12 '19 at 16:15
  • @LightnessRaceswithMonica, perhaps poorly phrased, but I wouldn't say it's wrong. It is warning you about a potential overflow when it is going to have to widen its type anyway; which I think is a fair warning. Just `double result = ( 2 * a );` generates the same warning. – ChrisMM Dec 12 '19 at 16:20
  • 2
    @LightnessRaceswithMonica it's not a bug. if `a` is INT_MAX for example, `2 * a` would overflow, but `2.0 * a` wouldn't. And the result is converted to `double` anyway for the division. So `2.0 * a` essentially gives the same result but it is much less likely to overflow. – Aykhan Hagverdili Dec 12 '19 at 17:23
  • 1
    Hmmmm actually yeah okay. – Lightness Races in Orbit Dec 12 '19 at 17:32
  • Does this answer your question? [Warning C26451: Arithmetic overflow](https://stackoverflow.com/questions/55995817/warning-c26451-arithmetic-overflow) – wovano Sep 18 '21 at 09:20

4 Answers4

4

It's not a bug. Here:

(-b - std::sqrt(d)) / (2 * a)

The result of the expression is a double. But result of 2 * a is an int and it's eventually converted to a double. It is possible that 2 * a overflows if a is too large before it's converted to double. But since the eventual result is already a double, you could cast 2 or a to double as well and avoid the risk of overflow once and for all. So the compiler is telling you that the Right ThingTM to do is:

(-b - std::sqrt(d)) / (2.0 * a)

It won't overflow (result of (2.0 * a) is a double) and it's probably already what you want.

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
  • 1
    Good explanation! But I want to add one remark: the compiler is not telling you want the Right Thing to do is. It's merely indicating a _possible_ problem and having _you_ decide how to deal with it. Static analyzers are not about true or false (good or bad code). I think they work best if you see them as a colleague that's asking you (without knowing all the details that you know): "have you really thought about this?" In some cases an additional cast can make the compiler warning go away (and also let your reviewing colleague know that you really thought about the statement). – wovano Sep 18 '21 at 08:34
3

The root cause here (pun very much accepted) is that the quadratic equation is not a Diophantine equation. It applies to real numbers, and in particular sqrt(d) is usually not an integer.

In C++, the return type of sqrt(IntegralType) is double. Thus 2*a is converted to double too, but only after multiplying. And Visual Studio very reasonably notes that you're better off doing the conversion before multiplying. It just doesn't note that you can even make a,b,c all doubles from the start.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Why does it recommend doing conversion *before* multiplying? –  Dec 12 '19 at 16:49
  • 1
    @khaliyeva: Multiplying two integers can overflow; converting them to `double` certainly won't, and multiplying the resulting `double`s won't overflow either. E.g. `a==INT_MAX` would cause an overflow and **Undefined Behavior**. – MSalters Dec 12 '19 at 16:51
1

The way to fix this is to static_cast<long long> because long long is 8 bytes and it is large enough to hold the information in the event of an overflow. An overflow occurs when you try to hold a number when you do not have enough bits to do so and as a result some of them get chopped off.

Here is an example without the warning:

std::cout << "Two roots: " << (-b - std::sqrt(d)) / (2 * static_cast<long long>(a)) << " "
          << (-b + std::sqrt(d)) / (2 * static_cast<long long>(a));

bhristov
  • 3,137
  • 2
  • 10
  • 26
-1

It no longer shows the warning in VS2022.

Apparently Microsoft concluded it produces more noise than useful information.

See my answer to the similar problem you linked to here: Warning C26451: Arithmetic overflow

doug
  • 3,840
  • 1
  • 14
  • 18