Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sqrt, perfect squares and floating point errors

In the sqrt function of most languages (though here I'm mostly interested in C and Haskell), are there any guarantees that the square root of a perfect square will be returned exactly? For example, if I do sqrt(81.0) == 9.0, is that safe or is there a chance that sqrt will return 8.999999998 or 9.00000003?

If numerical precision is not guaranteed, what would be the preferred way to check that a number is a perfect square? Take the square root, get the floor and the ceiling and make sure they square back to the original number?

Thank you!

like image 368
gnuvince Avatar asked Jan 04 '13 05:01

gnuvince


People also ask

Why is 4592 not a perfect square?

Answer. Explanation: 4592 is not a perfect square as it cannot be expressed as n × n form the product of two equal integer and the number that have 2,3,7 or 8 in the units place are not perfect squares. Hence, the given number 4592 is not a perfect square.

Why is 1000 not a perfect square?

We observe that 2 × 3 occur without pairs. Therefore, 1000 is not a perfect square.

Can decimal numbers be perfect squares?

When a decimal can be written as a fraction that is a perfect square, then the decimal is also a perfect square. The square root is a terminating or repeating decimal.

Why are perfect squares important?

Perfect squares are important because they're an example of how to take the square root of a perfectly precise natural number. The square root of a perfect square must also be a natural number, meaning that it's a non-decimal, non-fractional integer.


2 Answers

In IEEE 754 floating-point, if the double-precision value x is the square of a nonnegative representable number y (i.e. y*y == x and the computation of y*y does not involve any rounding, overflow, or underflow), then sqrt(x) will return y.

This is all because sqrt is required to be correctly-rounded by the IEEE 754 standard. That is, sqrt(x), for any x, will be the closest double to the actual square root of x. That sqrt works for perfect squares is a simple corollary of this fact.

If you want to check whether a floating-point number is a perfect square, here's the simplest code I can think of:

int issquare(double d) {
  if (signbit(d)) return false;
  feclearexcept(FE_INEXACT);
  double dd = sqrt(d);
  asm volatile("" : "+x"(dd));
  return !fetestexcept(FE_INEXACT);
}

I need the empty asm volatile block that depends on dd because otherwise your compiler might be clever and "optimise" away the calculation of dd.

I used a couple of weird functions from fenv.h, namely feclearexcept and fetestexcept. It's probably a good idea to look at their man pages.

Another strategy that you might be able to make work is to compute the square root, check whether it has set bits in the low 26 bits of the mantissa, and complain if it does. I try this approach below.

And I needed to check whether d is zero because otherwise it can return true for -0.0.

EDIT: Eric Postpischil suggested that hacking around with the mantissa might be better. Given that the above issquare doesn't work in another popular compiler, clang, I tend to agree. I think the following code works:

int _issquare2(double d) {
  if (signbit(d)) return 0;
  int foo;
  double s = sqrt(d);
  double a = frexp(s, &foo);
  frexp(d, &foo);
  if (foo & 1) {
    return (a + 33554432.0) - 33554432.0 == a && s*s == d;
  } else {
    return (a + 67108864.0) - 67108864.0 == a;
  }
}

Adding and subtracting 67108864.0 from a has the effect of wiping the low 26 bits of the mantissa. We will get a back exactly when those bits were clear in the first place.

like image 193
tmyklebu Avatar answered Oct 25 '22 10:10

tmyklebu


According to this paper, which discusses proving the correctness of IEEE floating-point square root:

The IEEE-754 Standard for Binary Floating-Point Arithmetic [1] requires that the result of a divide or square root operation be calculated as if in infinite precision, and then rounded to one of the two nearest floating-point numbers of the specified precision that surround the infinitely precise result

Since a perfect square that can be represented exactly in floating-point is an integer and its square root is an integer that can be precisely represented, the square root of a perfect square should always be exactly correct.

Of course, there's no guarantee that your code will execute with a conforming IEEE floating-point library.

like image 44
Gabe Avatar answered Oct 25 '22 09:10

Gabe