6

Is there any faster or more direct way of computing the integer square root:

http://en.wikipedia.org/wiki/Integer_square_root

in C# as

private long LongSqrt(long value)
{
    return Convert.ToInt64(Math.Sqrt(value));
}

?

J Fabian Meier
  • 33,516
  • 10
  • 64
  • 142
  • 2
    http://blog.wouldbetheologian.com/2011/11/fast-approximate-sqrt-method-in-c.html – qqbenq May 15 '14 at 07:43
  • 1
    @qqbenq If I understand the source correctly, it is fast approximate way of calculating a floating point square root. My question was more like: Can I save time by avoiding the conversion long -> double -> long? – J Fabian Meier May 15 '14 at 07:53
  • The conversions happen only once, while the sqrt algorithm is iterative. Unless this is *extremely* critical code, performance-wise, I wouldn't bother. – Rik May 15 '14 at 09:50
  • 1
    Also, "integer square root" usually refers to "the integer value of the actual square root" i.e rounded down. So I would suggest you consider using `Math.Floor`. In your current code `Math.Round` is redundant, because it's already being done in `Convert.ToInt64`. – Rik May 15 '14 at 10:02
  • FYI - "Convert.ToInt64(Math.Sqrt(value));" is only accurate for numbers under 26 bits. It will start to fail at 67108864 by being off by +/- 1. – SunsetQuest Jul 01 '23 at 19:04

2 Answers2

4

If you know the range in advance you can create a lookup index for a squared value and its integer square root.

Here is some simple code:

// populate the lookup cache
var lookup = new Dictionary<long, long>();
for (int i = 0; i < 20000; i++)
{
    lookup[i * i] = i;
}

// build a sorted index
var index = new List<long>(lookup.Keys);
index.Sort();

// search for a sample 27 
var foundIndex = index.BinarySearch(27);
if (foundIndex < 0)
{
    // if there was no direct hit, lookup the smaller value
    // TODO please check for out of bounds that might happen
    Console.WriteLine(lookup[index[~foundIndex - 1]]);
}
else
{
    Console.WriteLine(lookup[foundIndex]);
}

// yields 5

You can get around the dictionary lookup by creating a parallel second list, if you want it to be more efficient.

Thomas Jungblut
  • 20,854
  • 6
  • 68
  • 91
1

(Years late but maybe this will help someone else. However, I have spent some time on this topic.)

Fastest approximate square root (just like the one listed)...

// ApproxSqrt - Result can be +/- 1
static long ApproxSqrt(long x)
{
    if (x < 0)
        throw new ArgumentOutOfRangeException("Negitive values are not supported.");

    return (long)Math.Sqrt(x);
}

If you want something that would be accurate to all 64 bits...

// Fast Square Root for Longs / Int64 - Ryan Scott White 2023 - MIT License
static long Sqrt(long x)
{
    if (x < 0)
        throw new ArgumentOutOfRangeException("Negitive values are not supported.");

    long vInt = (long)Math.Sqrt(x);
    if (vInt * vInt > x)
        vInt--;
    return vInt;
}

For ulong / UInt64 ...

// Fast Square Root for Unsigned Longs - Ryan Scott White 2023 - MIT License
static ulong Sqrt(ulong x)
{
    ulong vInt = (ulong)Math.Sqrt(x);
    ulong prod = vInt * vInt;
    if (prod > x || prod == 0)
        vInt--;
    return vInt;
}
SunsetQuest
  • 8,041
  • 2
  • 47
  • 42