Home > OS >  Is there a value of type `double`, `K`, such that `K * K == 3.0`?
Is there a value of type `double`, `K`, such that `K * K == 3.0`?

Time:06-06

Is there a value of type double (IEEE 64-bit float / binary64), K, such that K * K == 3.0? (The irrational number is of course "square root of 3")

I tried:

static constexpr double Sqrt3 = 1.732050807568877293527446341505872366942805253810380628055806;
static_assert(Sqrt3 * Sqrt3 == 3.0);

but the static assert fails.

(I'm guessing neither the next higher nor next lower floating-point representable number square to 3.0 after rounding? Or is the parser of the floating point literal being stupid? Or is it doable in IEEE standard but fast math optimizations are messing it up?)

I think the digits are right:

$ python

>>> N = 1732050807568877293527446341505872366942805253810380628055806
>>> N * N
2999999999999999999999999999999999999999999999999999999999996\
607078976886330406910974461358291614910225958586655450309636

Update

I've discovered that:

static_assert(Sqrt3 * Sqrt3 < 3.0); // pass
static_assert(Sqrt3 * Sqrt3 > 2.999999999999999); // pass
static_assert(Sqrt3 * Sqrt3 > 2.9999999999999999); // fail

So the literal must produce the next lower value.

I guess I need to check the next higher value. Could bit-dump the representation maybe and then increment the last bit of the mantissa.

Update 2

For posterity: I wound up going with this for the Sqrt3 constant and the test:

static constexpr double Sqrt3 = 1.7320508075688772;
static_assert(0x1.BB67AE8584CAAP 0 == 1.7320508075688772);
static_assert(Sqrt3 * Sqrt3 == 2.9999999999999996);

CodePudding user response:

The answer is no; there is no such K.

The closest binary64 value to the actual square root of 3 is equal to 7800463371553962 × 2-52. Its square is:

60847228810955004221158677897444 × 2-104

This value is not exactly representable. It falls between (3 - 2-51) and 3, which are respectively equal to

60847228810955002264642499117056 × 2-104

and

60847228810955011271841753858048 × 2-104

As you can see, K * K is much closer to 3 - 2-51 than it is to 3. So IEEE 754 requires the result of the operation K * K to yield 3 - 2-51, not 3. (The compiler might convert K to an extended-precision format for the calculation, but the result will still be 3 - 2-51 after conversion back to binary64.)

Furthermore, if we go to the next representable value after K in the binary64 format, we will find that its square is closest to 3   2-51, which is the next representable value after 3.

This result should not be too surprising; in general, incrementing a number by 1 ulp will increment its square by roughly 2 ulps, so you have about a 50% chance, given some value x, that there is a K with the same precision as x such that K * K == x.

CodePudding user response:

The C standard does not dictate the default rounding mode. While it is typically round-to-nearest, ties-to-even, it could be round-upward, and some implementations support changing the mode. In such case, squaring 1.732050807568877193176604123436845839023590087890625 while rounding upward produces exactly 3.

#include <fenv.h>
#include <math.h>
#include <stdio.h>

#pragma STDC FENV_ACCESS ON


int main(void)
{
    volatile double x = 1.732050807568877193176604123436845839023590087890625;
    fesetround(FE_UPWARD);
    printf("%.99g\n", x*x);  // Prints “3”.
}

x is declared volatile to prevent the compiler from computing x*x at compile-time with a different rounding mode. Some compilers do not support #pragma STDC FENV_ACCESS but may support fesetround once the #pragma line is removed.

CodePudding user response:

Testing with Python is valid I think, since both use the IEEE-754 representation for doubles along with the rules for operations on same.

The closest possible double to the square root of 3 is slightly low.

>>> Sqrt3 = 3**0.5
>>> Sqrt3*Sqrt3
2.9999999999999996

The next available value is too high.

>>> import numpy as np
>>> Sqrt3p = np.nextafter(Sqrt3,999)
>>> Sqrt3p*Sqrt3p
3.0000000000000004

If you could split the difference, you'd have it.

>>> Sqrt3*Sqrt3p
3.0

CodePudding user response:

In the Ruby language, the Float class uses "the native architecture's double-precision floating point representation" and it has methods named prev_float and next_float that let you iterate through different possible floats using the smallest possible steps. Using this, I was able to do a simple test and see that there is no double (at least on x86_64 Linux) that meets your criterion. The Ruby interpreter is written in C, so I think my results should be applicable to the C double type.

Here is the Ruby code:

x = Math.sqrt(3)
4.times { x = x.prev_float }
9.times do
  puts "%.20f squared is %.20f" % [x, x * x]
  puts "Success!" if x * x == 3
  x = x.next_float
end

And the output:

1.73205080756887630500 squared is 2.99999999999999644729
1.73205080756887652704 squared is 2.99999999999999733546
1.73205080756887674909 squared is 2.99999999999999822364
1.73205080756887697113 squared is 2.99999999999999866773
1.73205080756887719318 squared is 2.99999999999999955591
1.73205080756887741522 squared is 3.00000000000000044409
1.73205080756887763727 squared is 3.00000000000000133227
1.73205080756887785931 squared is 3.00000000000000177636
1.73205080756887808136 squared is 3.00000000000000266454

CodePudding user response:

sqrt(3) is irrational, which means that there is no rational number k such that k*k == 3. A double can only represent rational numbers; therefore, there is no double k such that k*k == 3.

If you can accept a number that is close to satisfying k*k == 3, then you can use std::numeric_limits (in <type_traits>, if memory serves) to see if you’re within some minimal interval around 3. It may look like:

assert( abs(k*k - 3.) <= abs(k*k   3.) * std::numeric_limits<double>::epsilon * X);

Epsilon is the smallest difference from one that double can represent. We scale it by the sum of the two values to compare in order to bring its magnitude in line with the numbers we’re checking. X is a scaling factor that lets you adjust the precision you accept.

If this is a theoretical question: no. If it’s a practical question: yes, up some level of precision.

CodePudding user response:

this is a simple square root function...

double K=3,ans=0;

ans=sqrt(K)

  • Related