views:

420

answers:

4

I am wondering if this is true: When I take the square root of a squared integer, like in

f = Math.sqrt(123*123)

I will get a floating point number very close to 123. Due to floating point representation precision, this could be something like 122.99999999999999999999 or 123.000000000000000000001.

Since floor(122.999999999999999999) is 122, I should get 122 instead of 123. So I expect that floor(sqrt(i*i)) == i-1 in about 50% of the cases. Strangely, for all the numbers I have tested, floor(sqrt(i*i) == i. Here is a small ruby script to test the first 100 million numbers:

100_000_000.times do |i|
  puts i if Math.sqrt(i*i).floor != i
end

The above script never prints anything. Why is that so?

UPDATE: Thanks for the quick reply, this seems to be the solution: According to wikipedia

Any integer with absolute value less than or equal to 2^24 can be exactly represented in the single precision format, and any integer with absolute value less than or equal to 2^53 can be exactly represented in the double precision format.

Math.sqrt(i*i) starts to behave as I've expected it starting from i=9007199254740993, which is 2^53 + 1.

+15  A: 

For "small" integers, there is usually an exact floating-point representation.

Anon.
Yes. The floating point representation is exact for all integers having fewer significant bits than the mantissa of the floating point representation: 23 bits for single precision, 52 for double if we're talking about IEEE 754. Thanks to an implied leading 1 in the mantissa, it's actually 24 and 53.
Carl Smotricz
+1: As a rule of thumb, think of floats as having 48 useful bits of precision. (Actual sizes vary, but you can work with 48 as a general rule). In order to get to a place where the last bit actually matters, you need to be working with a product of ints that's at least 48 bits in size.
S.Lott
thanks, you're right. It starts to stop working at 9007199254740993 which is `2^53 + 1`
martinus
Woo hoo, thanks for checking! S.Lott had me worried there for a while! :)
Carl Smotricz
+6  A: 

It's not too hard to find cases where this breaks down as you'd expect:

Math.sqrt(94949493295293425**2).floor
# => 94949493295293424
Math.sqrt(94949493295293426**2).floor
# => 94949493295293424
Math.sqrt(94949493295293427**2).floor
# => 94949493295293424
tadman
And `Math.sqrt(94949493295293423**2).floor` ==> `94949493295293424`, too.
mobrule
+3  A: 

Ruby's Float is a double-precision floating point number, which means that it can accurately represent numbers with (rule of thumb) about 16 significant decimal digits. For regular single-precision floating point numbers it's about significant 7 digits.

You can find more information here:

What Every Computer Scientist Should Know About Floating-Point Arithmetic: http://docs.sun.com/source/819-3693/ncg_goldberg.html

psyho
+17  A: 

Here's the essence of your confusion:

Due to floating point representation precision, this could be something like 122.99999999999999999999 or 123.000000000000000000001.

This is false. It will always be exactly 123 on a IEEE-754 compliant system, which is almost all systems in these modern times. Floating-point arithmetic does not have "random error" or "noise". It has precise, deterministic rounding, and many simple computations (like this one) do not incur any rounding at all.

123 is exactly representable in floating-point, and so is 123*123 (so are all modest-sized integers). So no rounding error occurs when you convert 123*123 to a floating-point type. The result is exactly 15129.

Square root is a correctly rounded operation, per the IEEE-754 standard. This means that if there is an exact answer, the square root function is required to produce it. Since you are taking the square root of exactly 15129, which is exactly 123, that's exactly the result you get from the square root function. No rounding or approximation occurs.

Now, for how large of an integer will this be true?

Double precision can exactly represent all integers up to 2^53. So as long as i*i is less than 2^53, no rounding will occur in your computation, and the result will be exact for that reason. This means that for all i smaller than 94906265, we know the computation will be exact.

But you tried i larger than that! What's happening?

For the largest i that you tried, i*i is just barely larger than 2^53 (1.1102... * 2^53, actually). Because conversions from integer to double (or multiplication in double) are also correctly rounded operations, i*i will be the representable value closest to the exact square of i. In this case, since i*i is 54 bits wide, the rounding will happen in the very lowest bit. Thus we know that:

i*i as a double = the exact value of i*i + rounding

where rounding is either -1,0, or 1. If rounding is zero, then the square is exact, so the square root is exact, so we already know you get the right answer. Let's ignore that case.

So now we're looking at the square root of i*i +/- 1. Using a Taylor series expansion, the infinitely precise (unrounded) value of this square root is:

i * (1 +/- 1/(2i^2) + O(1/i^4))

Now this is a bit fiddly to see if you haven't done any floating point error analysis before, but if you use the fact that i^2 > 2^53, you can see that the:

1/(2i^2) + O(1/i^4)

term is smaller than 2^-54, which means that (since square root is correctly rounded, and hence its rounding error must be smaller than 2^54), the rounded result of the sqrt function is exactly i.

It turns out that (with a similar analysis), for any exactly representable floating point number x, sqrt(x*x) is exactly x (assuming that the intermediate computation of x*x doesn't over- or underflow), so the only way you can encounter rounding for this type of computation is in the representation of x itself, which is why you see it starting at 2^53 + 1 (the smallest unrepresentable integer).

Stephen Canon
Wow. Very thorough (and accurate) treatment, +1.
Carl Smotricz