Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where are the inaccuracies in math.sqrt() and math.pow() coming from for large numbers? [duplicate]

Tags:

python

pow

sqrt

If you take a number, take its square root, drop the decimal, and then raise it to the second power, the result should always be less than or equal to the original number.

This seems to hold true in python until you try it on 99999999999999975425 for some reason.

import math

def check(n):
    assert math.pow(math.floor(math.sqrt(n)), 2) <= n

check(99999999999999975424)  # No exception.
check(99999999999999975425)  # Throws AssertionError.

It looks like math.pow(math.floor(math.sqrt(99999999999999975425)), 2) returns 1e+20.

I assume this has something to do with the way we store values in python... something related to floating point arithmetic, but I can't reason about specifically how that affects this case.

like image 661
Evan Rose Avatar asked Jan 22 '18 01:01

Evan Rose


People also ask

How do you find the square root in math POW?

pow(double a) method accepts one argument of the double data type and returns a value which is the square root of the argument. where a is the number whose square root is to be found. Let us see a program where we find the square root of number using the Math. sqrt() method.

What type of value is returned by math sqrt ()?

Math. sqrt() returns the square root of a value of type double passed to it as argument. If the argument is NaN or negative, then the result is NaN.

Does math sqrt return a double?

The sqrt function returns a double value (for natural reasons which I'm sure that you understand). This value is typically represented in 8 bytes, in floating-point format (as specified by the standard). It doesn't have decimal digits in the way that you see them.

What does math sqrt do?

The Math. sqrt() method returns the square root of a number.


2 Answers

The problem is not really about sqrt or pow, the problem is you're using numbers larger than floating point can represent precisely. Standard IEEE 64 bit floating point arithmetic can't represent every integer value beyond 52 bits (plus one sign bit).

Try just converting your inputs to float and back again:

>>> int(float(99999999999999975424))
99999999999999967232
>>> int(float(99999999999999975425))
99999999999999983616

As you can see, the representable value skipped by 16384. The first step in math.sqrt is converting to float (C double), and at that moment, your value increased by enough to ruin the end result.

Short version: float can't represent large integers precisely. Use decimal if you need greater precision.

like image 70
ShadowRanger Avatar answered Sep 26 '22 03:09

ShadowRanger


Unlike Evan Rose's (now-deleted) answer claims, this is not due to an epsilon value in the sqrt algorithm.

Most math module functions cast their inputs to float, and math.sqrt is one of them.

99999999999999975425 cannot be represented as a float. For this input, the cast produces a float with exact numeric value 99999999999999983616, which repr shows as 9.999999999999998e+19:

>>> float(99999999999999975425)
9.999999999999998e+19
>>> int(_)
99999999999999983616L

The closest float to the square root of this number is 10000000000.0, and that's what math.sqrt returns.

like image 35
user2357112 supports Monica Avatar answered Sep 26 '22 03:09

user2357112 supports Monica