4

In C++ the sqrt function operates only with double values.

If we use integers (unsigned long long) can we be sure that

x == sqrt(x * x)

for any positive x where x * x <= MAXIMUM_VALUE?

Is it depend on the machine architecture and compiler?

Jegors Čemisovs
  • 608
  • 6
  • 14
  • One thing this depends on is `sizeof(integer_type)` you are using and the `sizeof(floating_point_type)`. For instance you couldn't use a 64 bit integer with a 32 bit floating point type. – NathanOliver Feb 16 '21 at 15:49
  • 2
    Interesting question, but "the sqrt function operate only with double values"? [C (and therefore C++) provides functions to compute square roots for `float`, `double`, and `long double`](https://port70.net/~nsz/c/c11/n1570.html#7.12.7.5). – Andrew Henle Feb 16 '21 at 15:51
  • 5
    Pick a language. The answer will be different for each. – dbush Feb 16 '21 at 15:52
  • @dbush: Will it? Floating point and integer representations are the same for all three languages (more or less). – Robert Harvey Feb 16 '21 at 15:53
  • @TedLyngmo I don't think it can be always true. Maybe the optimizer can deduce these *should* be equal though. – Eugene Sh. Feb 16 '21 at 15:54
  • @TedLyngmo If you use the same integer type for the cast (i.e. `x == (long) sqrt(x * x)`), it [seems to work](https://godbolt.org/z/sPfsa3). – IlCapitano Feb 16 '21 at 16:01
  • @IlCapitano Indeed. I didn't notice the type diff :-) Removing my ramblings. – Ted Lyngmo Feb 16 '21 at 16:01
  • 2
    I don't see C standard or even IEEE 754 have any implementation constraints or special requirements on `sqrt` . – Eugene Sh. Feb 16 '21 at 16:02
  • yes. `x == sqrt(x*x)` is true for all doubles, so it's true for all integers representable in `double`. See [square root of square of x equals x](https://stackoverflow.com/q/42768404/995714) – phuclv Feb 16 '21 at 16:07
  • 1
    Does this answer your question? [IEEE double such that sqrt(x\*x) ≠ x](https://stackoverflow.com/questions/41656438/ieee-double-such-that-sqrtxx-%e2%89%a0-x) – phuclv Feb 16 '21 at 16:08
  • 1
    Possible duplicate: [C++ sqrt function precision for full squares](https://stackoverflow.com/questions/20137105/c-sqrt-function-precision-for-full-squares) – rustyx Feb 16 '21 at 16:10
  • 1
    C and C++ have various nasty implicit promotion rules. And more obviously, `x = INT_MAX` will cause this to invoke undefined behavior. One question per language please, you are asking 2 unrelated questions here (C and C++ happens to behave the same). – Lundin Feb 16 '21 at 16:21
  • 1
    Here is C program printing first `int` which does not satisfy it - yes it exists and equals to `46341` on this implementation: https://ideone.com/P5vtFB – Eugene Sh. Feb 16 '21 at 16:37
  • @jegors-Čemisovs, `BigInteger` will support a majority of use cases (not the majority of numbers as `Infinity` is much bigger). Please check my [answer](https://stackoverflow.com/questions/66227620/can-we-assume-that-x-intsqrtx-x-for-all-positive-integers/66299511#66299511)(if it solves the use case) – Thiyanesh Feb 21 '21 at 06:10

5 Answers5

7

In Java, Math.sqrt(x) takes a double value. You stated that x is such that x * x is below Integer.MAX_VALUE. Every integer is perfectly representable in double - double in java is explicitly defined as an iEEE-754 style double with a 52-bit mantissa; therefore in java a double can perfectly represent all integral values between -2^52 and +2^52, which easily covers all int values (as that is defined as signed 32-bit on java), but it does not cover all long values. (Defined as signed 64-bit; 64 is more than 52, so no go).

Thus, x * x loses no precision when it ends up getting converted from int to double. Then, Math.sqrt() on this number will give a result that is also perfectly representable as a double (because it is x, and given that x*x fits in an int, x must also fit), and thus, yes, this will always work out for all x.

But, hey, why not give it a shot, right?

public static void main(String[] args) {
    int i = 1;
    while (true) {
        if (i * i < 0) break;
        int j = (int) Math.sqrt(i * i);
        if (i != j) System.out.println("Oh dear! " + i + " -> " + j);

        i++;
    }
    System.out.println("Done at " + i);
}

> Done at 46341

Thus proving it by exhaustively trying it all.

Turns out, none exist - any long value such that x * x still fits (thus, is <2^63-1) has the property that x == (long) Math.sqrt(x * x);. This is presumably because at x*x, the number fits perfectly in a long, even if not all integer numbers that are this large do. Proof:

long p = 2000000000L;
for (; true; p++) {
    long pp = p * p;
    if (pp < 0) break;
    long q = (long) Math.sqrt(pp);
    if (q != p) System.out.println("PROBLEM: " + p + " -> " + q);
}
System.out.println("Abort: " + p);

> Abort: 3037000500

Surely if any number exists that doesn't hold, there is at least one in this high end range. Starting from 0 takes very long.

But do we know that sqrt will always return an exact value for a perfect square, or might it be slightly inaccurate?

We should - it's java. Unlike C, almost everything is 'well defined', and a JVM cannot legally call itself one if it fails to produce the exact answer as specified. The leeway that the Math.sqrt docs provide is not sufficient for any answer other than precisely x to be a legal implementation, therefore, yes, this is a guarantee.

In theory the JVM has some very minor leeway with floating point numbers, which strictfp disables, but [A] that's more about using 80-bit registers to represent numbers instead of 64, which cannot possibly ruin this hypothesis, and [B] a while back a java tag question showed up to show strictfp having any effect on any hardware and any VM version and the only viable result was a non-reproducible thing from 15 years ago. I feel quite confident to state that this will always hold, regardless of hardware or VM version.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
  • 1
    Could you provide any `long` value for which this does not hold? Yes, not all `long` values are representable with `double`, but the rounding error may also be lost when the result is converted back to `long`. – Daniel Langr Feb 16 '21 at 16:05
  • But do we know that `sqrt` will always return an exact value for a perfect square, or might it be slightly inaccurate? – Tom Hawtin - tackline Feb 16 '21 at 16:06
  • 1
    "EDIT" monikers are not necessary on Stack Overflow. Every post on Stack Overflow has a detailed edit history that anyone can view. The edit history for this post is located [here](https://stackoverflow.com/posts/66227932/revisions). – Robert Harvey Feb 16 '21 at 16:34
  • "in java a double can perfectly represent all integral values between -2^52 and +2^52" <- true, but you could also replace that "52" with a "53" and the statement would still be true. :-) – Mark Dickinson Feb 16 '21 at 16:41
  • @RobertHarvey But there's the five-minute(?) grace period where edits don't show up in history. An "EDIT" moniker for an edit in the grace period that invalidates comments would be a nice thing... – Andrew Henle Feb 16 '21 at 16:54
  • @AndrewHenle: If the edit occurs in a five minute window, it's not worth mentioning. – Robert Harvey Feb 16 '21 at 17:13
0

Just try it. Yes it works in Java, for non-negative numbers. Even works for long contrary to common opinion.

class Code {
    public static void main(String[] args) throws Throwable {
        for (long x=(long)Math.sqrt(Long.MAX_VALUE);; --x) {
            if (!(x == (long)Math.sqrt(x * x))) {
                System.err.println("Not true for: "+x);
                break;
            }
        }
        System.err.println("Done");
    }
}

(The first number that doesn't work is 3037000500L which goes negative when squared.)

Even for longs, the range of testable values is around 2^31 or 2*10^9 so for something this trivial it is reasonable to check every single value. You can even brute force reasonable cryptographic functions for 32-bit values - something more people should realise. Won't work so well for the full 64 bits.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
0

I think we can believe.

Type casting a floating point number to an integer is to take only integer part of it. I believe you may concern, for example, sqrt(4) yields a floating point number like 1.999...9 and it is type casted to 1. (Yielding 2.000...1 is fine because it will be type casted to 2.)

But the floating number 4 is like

(1 * 2-0 + 0 + 2-1 + ... + 0 * 2-23) * 22

according to Floating-point arithmetic.

Which means, it must not be smaller than 4 like 3.999...9. So also, sqrt of the number must not be smaller than

(1 * 2-0) * 2

So sqrt of a square of an integer will at least yield a floating point number greater than but close enough to the integer.

ghchoi
  • 4,812
  • 4
  • 30
  • 53
  • 4 happens to be an easy case, which is exactly representable as a float. But consider `2^32-1` squared, which is `(2^64-2^33+1)` It obviously can't be represented exactly in 23 or 52 bits. – MSalters Feb 15 '22 at 12:18
0

BigInteger - sqrt(since 9)

  1. Use cases requiring tighter constraint over possibilities of overflow can use BigInteger
  2. BigInteger should work for any practical use case.
  3. Still for normal use case, this might not be efficient.

Constraints

BigInteger Limits

BigInteger must support values in the range -2^Integer.MAX_VALUE (exclusive) to +2^Integer.MAX_VALUE (exclusive) and may support values outside of that range. An ArithmeticException is thrown when a BigInteger constructor or method would generate a value outside of the supported range. The range of probable prime values is limited and may be less than the full supported positive range of BigInteger. The range must be at least 1 to 2500000000.
Implementation Note:

In the reference implementation, BigInteger constructors and operations throw ArithmeticException when the result is out of the supported range of -2^Integer.MAX_VALUE (exclusive) to +2^Integer.MAX_VALUE (exclusive).

Array size limit when initialized as byte array

String length limit when initialized as String

Definitely may not support 1/0

jshell> new BigInteger("1").divide(new BigInteger("0"))
|  Exception java.lang.ArithmeticException: BigInteger divide by zero
|        at MutableBigInteger.divideKnuth (MutableBigInteger.java:1178)
|        at BigInteger.divideKnuth (BigInteger.java:2300)
|        at BigInteger.divide (BigInteger.java:2281)
|        at (#1:1)

An example code

import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;

public class SquareAndSqrt {

    static void valid() {
        List<String> values = Arrays.asList("1", "9223372036854775807",
            "92233720368547758079223372036854775807", 
            new BigInteger("2").pow(Short.MAX_VALUE - 1).toString());

        for (String input : values) {
            final BigInteger value = new BigInteger(input);
            final BigInteger square = value.multiply(value);
            final BigInteger sqrt = square.sqrt();

            System.out.println("value: " + input + System.lineSeparator()
                + ", square: " + square + System.lineSeparator()
                + ", sqrt: " + sqrt + System.lineSeparator()
                + ", " + value.equals(sqrt));

            System.out.println(System.lineSeparator().repeat(2)); // pre java 11 - System.out.println(new String(new char[2]).replace("\0", System.lineSeparator()));
        }
    }

    static void mayBeInValid() {
        try {
            new BigInteger("2").pow(Integer.MAX_VALUE);
        } catch (ArithmeticException e) {
            System.out.print("value: 2^Integer.MAX_VALUE, Exception: " + e);
            System.out.println(System.lineSeparator().repeat(2));
        }
    }

    public static void main(String[] args) {
        valid();
        mayBeInValid();
    }
}
Thiyanesh
  • 2,360
  • 1
  • 4
  • 11
-1

in cmath library sqrt function always convert argument to double or float so the range of double or float much more than unsigned long long so it always give positive. for reference you can use https://learn.microsoft.com/en-us/cpp/standard-library/cmath?view=msvc-170, https://learn.microsoft.com/en-us/cpp/c-language/type-float?view=msvc-170

  • 2
    This is incorrect. The range is bigger, but the precision is less (52 bits versus 64). Note: sizes taken from the linked VC++; other implementations could have somewhat different precisions. – MSalters Feb 15 '22 at 12:11