• Martin Thoma
  • Home
  • Categories
  • Tags
  • Archives
  • Support me

Calculate square roots

Contents

  • Input and output
  • Reference code
  • Newton's method
  • Exponential identity
  • See also

Suppose you have an equation like this:

$x^2 = a, \;\;\;\; a \in \mathbb{R}_{\geq 0}$

Your task is to compute the solution $x \in \mathbb{R}_{\geq 0}$.

How do you solve this algorithmically?

Input and output

Write a program that takes two parameters $a, n \in \mathbb{N}_{\geq 1}$:

  • $a$: The number you should take the square root of. $a$ is not bigger than 65535. To make this a little bit simpler, you can also assume that $a$ is an integer.
  • $n$: Number of iterations you may use

Your program should output exactly one positive floating point number. The decimal separator is a point. At the end of the number should be a newline character \n.

Reference code

// Thanks to http://stackoverflow.com/a/15363123/562769
#include <stdio.h>
#include <gmp.h>

int main(int argc, char *argv[]) {
    mpf_t res, a;
    mpf_set_default_prec(1000000); // Increase this number.
    mpf_init(res);
    mpf_init(a);
    mpf_set_str(a, "2", 10);
    mpf_sqrt (res, a);
    gmp_printf("%.1000Ff\n\n", res); // Increase this number.
    return 0;
}

You need GMP (libgmp-dev) to compile this.

Compile it like this:

gcc sqrt-reference.c  -lgmp -lm -O0 -g3 -o reference.out

This is the script I use to get the number of correct digits:

#!/usr/bin/env python
# -*- coding: utf-8 -*-


def getScore(program, a, n):
    import os

    os.system("./reference.out " + str(a) + " > reference.txt")
    os.system("./" + program + " " + str(a) + " " + str(n) + " > result.txt")

    f = open("reference.txt", "r")
    reference = f.read()
    f.close()

    f = open("result.txt", "r")
    result = f.read()
    f.close()

    points = 0
    areEqual = True
    while reference[points] != "\n" and result[points] != "\n":
        if reference[points] == result[points]:
            points += 1
        else:
            break
    if points >= 2:
        points -= 1  # decimal point
    return points


if __name__ == "__main__":
    from argparse import ArgumentParser

    parser = ArgumentParser()

    # Add more options if you like
    parser.add_argument(
        "-p",
        "--program",
        dest="program",
        help="your program",
        metavar="FILE",
        required=True,
    )
    parser.add_argument(
        "-a", metavar="A", type=int, required=True, help="calculate squre root of a"
    )
    parser.add_argument(
        "-n", metavar="N", type=int, required=True, help="maximum n iterations"
    )

    args = parser.parse_args()

print(
    "Points for a=%i and n=%i: %i"
    % (args.a, args.n, getScore(args.program, args.a, args.n))
)

Newton's method

How should be choose the initial value? I thought $\frac{a}{2}$ could be ok. In a good implementation you'll probably do this with a lookup table.

With long double:

#include <iostream>
#include <cmath>

using namespace std;

long double newton(int a, int n) {
    long double x = ((long double)a)/2;
    for (int i=0; i<n; i++) {
        x = x - (x*x - a)/(2*x);
    }
    return x;
}

int main(int argc, char *argv[]) {
    if (argc != 3) {
        cout << "Please enter exactly two arguments." << endl;
        return 1;
    }
    int a = atoi(argv[1]);
    int n = atoi(argv[2]);
    printf("%.80Lf\n", newton(a, n));
    return 0;
}

I failed to convert this to a version that uses GMP :-/

But it converges quite fast:

Newtons method for calculating square roots
Newtons method for calculating square roots

Exponential identity

According to Wikipedia (Source: Methods of computing square roots) many calculators use the following identity:

$\displaystyle \sqrt{a} = e^{\frac{1}{2}\ln a}$

But to calculate this, you need to be able to calculate $e^x$ for $x \in \mathbb{R}{\geq 0}$ and $\ln(a)$ for $a \in \mathbb{R}$.

I've used the definition of $e^x$ to calculate $e^x$: $\displaystyle e^x := \sum_{i = 0}^\infty \frac{x^i}{i!}$

and:

$\displaystyle \ln(1+x) =- \sum_{k=0}^\infty \frac{(-x)^{k+1}}{k+1}$

So I gave it a try:

#include <iostream>
#include <cmath>

using namespace std;

long double ln(int S, int n) {
    long double tmp = S - 1;
    long double result = tmp;
    long double sign = 1.0;
    for (int i=2; i<n/2; i++) {
        tmp *= (S-1);
        sign *= -1;
        result += sign*tmp/i;
    }
    return result;
}

long double e(long double x, int n) {
    long double numerator = 1;
    long double denominator = 1;
    long double result = 1;
    for (int i=1; i<n/2; i++) {
        numerator *= x;
        denominator *= i;
        result += numerator/denominator;
    }
    return result;
}

long double sqrt(int a, int n) {
    return e(ln(a, n)*0.5, n);
}

int main(int argc, char *argv[]) {
    if (argc != 3) {
        cout << "Please enter exactly two arguments." << endl;
        return 1;
    }
    int a = atoi(argv[1]);
    int n = atoi(argv[2]);
    printf("%.80Lf\n", sqrt(a, n));
    return 0;
}

This converges VERY slow: For $a = 2$

  • $n=1$: 1 digit correct
  • $n=10$: 1 digit correct
  • $n=100$: 2 digits correct
  • $n=1000$: 4 digit correct
  • $n=10,000$: 5 digit correct
  • $n=100,000$: 5 digit correct
  • $n=1,000,000$: 6 digit correct

Ok, lets take a better Taylor series for calculating $e^x$: $\displaystyle e^x = \sum_{k=0}^\infty \frac{x^{-1+2 k} (2 \cdot k+x)}{(2\cdot k)!}$

This didn't change anything! I'm very surprised ... as I've calculated $\pi$ for another article, changing the series to something similar improved the speed of convergence drastically.

See also

  • StackOverflow: How does the computer calculate Square roots?
  • Intel® 64 and IA-32 Architectures Software Developer’s Manual: FSQRT, SQRTPS, SQRTSS
  • Source code of this article

Feel free to add a program that calculates square roots. When they are interesting, I'll probably add them to this article.

Published

Jun 6, 2013
by Martin Thoma

Category

Cyberculture

Tags

  • CPP 10
  • GMP 1

Contact

  • Martin Thoma - A blog about Code, the Web and Cyberculture
  • E-mail subscription
  • RSS-Feed
  • Privacy/Datenschutzerklärung
  • Impressum
  • Powered by Pelican. Theme: Elegant by Talha Mansoor