-1

How can I square root a uInt64 in F#?

I've tried:

sqrt(1UL) // the type 'uint64' does not support the operator 'Sqrt'

//and

1UL**0.5 //the type 'uint64' does not support the operator 'Pow'

You can do integer exponents using pown, but it doesn't work to replace sqrt because the power can only be an int.

pown 4 2 // 16
farlee2121
  • 2,959
  • 4
  • 29
  • 41
  • Why not simply cast to `double`? – Nico Schertler Jan 05 '20 at 03:36
  • Please think a little bit about your question before posting. You would quickly find that you also cannot take the square root of an `int`. And it would also lead straight away to the question: what type would the square root of an `int` or `int64` have and how would you expect it to behave? – Charles Roddie Jan 05 '20 at 14:02
  • @CharlesRoddie I'm not a functional programmer, so maybe i'm not seeing what you're talking about, but I absolutely expect to be able to find the root of an int. For example, `sqrt(4) = 2` or `sqrt(3) = 1.73...`. I expect it to behave like the math function, i put in a number and get a number out without having to explicitly type everything – farlee2121 Jan 05 '20 at 16:04
  • @NicoSchertler If i'm not mistaken, a double is signed 64-bit number and a uInt64 an unsigned 64-bit number. Therefore, I think a uInt64 may not translate correctly to a double. – farlee2121 Jan 05 '20 at 16:11
  • What is the type of "the math function" you are talking about when you say "I expect it to behave like the math function": i.e. what is the domain and codomain. – Charles Roddie Jan 05 '20 at 17:04
  • Yes, casting can potentially lose precision. But is that precision relevant for your application? If so, you would need your own square root implementation. – Nico Schertler Jan 05 '20 at 17:13
  • @NicoSchertler Unfortunately I can't guarantee that the precision wouldn't matter and I can't change the interface argument type. It looks like this is a more generally hard issue than expected [issues with precision](https://codereview.stackexchange.com/questions/69069/computing-the-square-root-of-a-64-bit-integer) and [fundamental conversion for assembly](https://stackoverflow.com/questions/47683107/sqrt-of-uint64-t-vs-int64-t). A good reminder that computers handle numbers in a bounded finite way – farlee2121 Jan 05 '20 at 17:51
  • @CharlesRoddie Sqrt is a function over the complex numbers to the complex numbers. It can also be represented in a reduced fashion from the positive real numbers onto the real numbers – farlee2121 Jan 05 '20 at 18:00
  • That answers your question doesn't it? If we take the function from positive real numbers, `int` is not a good representation of the real numbers and that is why sqrt is not defined on `int`-like types. If it were defined, its output would be a `float`-like type, but there are various such types so it's not clear which one would be most natural. `sqrt` is defined on `float`-like types, where the output is the same type as the input. – Charles Roddie Jan 05 '20 at 20:31

2 Answers2

3

If you want only the integer part of the square root, your best bet is to define your own function for it. For your case (using unsigned long values), it could look as follows:

let rec sqrtul n =
    if n <= 0UL then 0UL else
        let r2 = (sqrtul (n >>> 2)) <<< 1
        let r3 = r2 + 1UL
        if n < r3 * r3 then r2 else r3

You can call it like this:

sqrtul 1UL

Alternative implementations can be found online.

If your goal is to get the full decimal value to the extent permitted by the chosen type, you will have to convert your initial value first. For example, if you need to have a float result, do the following:

sqrt (float 1UL)
dumetrulo
  • 1,993
  • 9
  • 11
  • Hmm. I'm looking for the full decimal value. I find it interesting that the input and output type have to be the same. Sadly tests on casting did hit precision issues for me. Because of that i'm hesitant to mark it as the answer, but you super deserve those upvotes – farlee2121 Jan 08 '20 at 03:57
  • If you need more precision, ``decimal`` instead of ``float`` or ``double`` might be the way to go. – dumetrulo Jan 08 '20 at 08:30
  • Sadly, decimal doesn't support sqrt either – farlee2121 Jan 08 '20 at 16:18
  • I found an implementation of sqrt for decimal https://stackoverflow.com/questions/4124189/performing-math-operations-on-decimal-datatype-in-c – farlee2121 Jan 08 '20 at 18:16
0

I found some answers when I started looking into decimal sqrt algorithms. Decimal allows for arbitrary precision, so you can use this for any number type.

Solution 1: Control precision through iterations (newton sqrt approximation)

let itrSqrt n maxIter = 
    let rec guess g i =
      match i with
      | 0 -> g
      | _ -> guess ((g + (n / g)) / 2M) (i - 1)
    guess (n / 2M) maxIter;;

Solution 2: Control precision with an epsilon (meaning the change between iterations) (Performing Math operations on decimal datatype in C#?)

let dSqrt (d:decimal) (epsilon:decimal): decimal =
    let rec guess previous =
        if previous = 0M then 0M
        else 
            let current = (previous + d / previous) / 2M
            if(abs(previous - current) < epsilon) 
            then current
            else guess(current)
    let initialGuess = decimal (sqrt(double d))
    guess(initialGuess)
farlee2121
  • 2,959
  • 4
  • 29
  • 41