Is it possible to use == on FP in this example?

I came across this code here .

Generators doubleSquares(int value) { Generators result; for (int i = 0; i <= std::sqrt(value / 2); i++) // 1 { double j = std::sqrt(value - std::pow(i, 2)); // 2 if (std::floor(j) == j) // 3 result.push_back( { i, static_cast<int>(j) } ); // 4 } return result; } 

Am I not mistaken in believing that // 3 is dangerous?

+8
c ++ floating-point
source share
3 answers

This code is not guaranteed by the C ++ standard to work as desired.

Some low-quality math libraries do not return correctly rounded values ​​for pow , even if the inputs have integer values, and the math result can be accurately represented. sqrt can also return an inaccurate value, although this function is easier to implement and therefore less likely to suffer from defects.

Thus, it is not guaranteed that j is an integer if you expect it to be.

In a quality math library, pow and sqrt will always return the correct results (zero error) when the math result is accurately represented. If you have a quality implementation in C ++, this code should work as you wish, up to the limits of integer types and floating point types used.


Code improvement

There is no reason to use pow in this code; std::pow(i, 2) must be i*i . This leads to exact arithmetic (up to the whole overflow) and completely eliminates the question of the correctness of pow .

Removing pow leaves only sqrt . If we know that the implementation returns the correct values, we can accept the use of sqrt . If not, we can use this instead:

 for (int i = 0; i*i <= value/2; ++i) { int j = std::round(std::sqrt(value - i*i)); if (j*j + i*i == value) result.push_back( { i, j } ); } 

This code relies only on sqrt to return a result within .5 that even a low-quality sqrt implementation should provide reasonable input values.

+10
source share

There are two different but related questions:

  • Is j an integer?
  • Is it possible that j will be the result of double computation, the exact result of which will be an integer?

The code quoted asks the first question. Wrong to ask the second question. A more specific context should be defined to be sure which question should be asked.

If the second question is asked, you cannot depend only on floor . Consider a double one that is greater than 2.99999999999, but less than 3. This can be a calculation result, the exact value of which will be 3. Its gender is 2, and it is more than its gender by almost 1. You will need to compare being closer to the std:round result std:round .

+1
source share

I would say that it is dangerous. You should always check the "equality" of floating point numbers by comparing the difference between two numbers with an acceptably small number, for example:

 #include <math.h> ... if (fabs(std::floor(j) - j) < eps) { ... 

... where eps is a number that is reasonably small for one purpose. This approach is necessary if operations are not guaranteed to return accurate results, which may be true in some cases (for example, systems compatible with IEEE-754), but the C ++ standard does not require this to be true. See, for example, Cross-platform issues with floating point arithmetic in C ++ .

0
source share

All Articles