Arbitrary precision for decimals square roots in g

2019-07-21 14:55发布

I am looking for a way to calculate a square root with an arbitrary precision (something like 50 digits after the dot).

In python, it is easily accessible with Decimal:

from decimal import *
getcontext().prec = 50
Decimal(2).sqrt() # and here you go my 50 digits

After seeing the power of math/big I skimmed through the documentation but have not found anything similar.

So is my only option is to write some sort of numerical computing method which will iteratively try to compute the answer?

标签: go precision
2条回答
聊天终结者
2楼-- · 2019-07-21 15:47

This is my own implementation of square root calculation. While waiting for answers, I decided to give methods of computing square roots a try. It has a whole bunch of methods but at the very end I found a link to a Square roots by subtraction pdf, which I really liked because the description of the algorithm is only a couple of lines (and I have not seen it before in comparison to Newton's method).

So here is my implementation (bigint is not really nice to work with in go):

func square(n int64, precision int64) string{
    ans_int := strconv.Itoa(int(math.Sqrt(float64(n))))

    limit   := new(big.Int).Exp(big.NewInt(10), big.NewInt(precision + 1), nil)
    a       := big.NewInt(5 * n)
    b       := big.NewInt(5)
    five    := big.NewInt(5)
    ten     := big.NewInt(10)
    hundred := big.NewInt(100)

    for b.Cmp(limit) < 0{
        if a.Cmp(b) < 0{
                a.Mul(a, hundred)
            tmp := new(big.Int).Div(b, ten)
            tmp.Mul(tmp, hundred)
            b.Add(tmp, five)
        } else {
            a.Sub(a, b)
            b.Add(b, ten)
        }
    }
    b.Div(b, hundred)

    ans_dec := b.String()

    return ans_dec[:len(ans_int)] + "." + ans_dec[len(ans_int):]
}

P.S. thank you Nick Craig-Wood for making the code better with your amazing comment.

And using it, one can find that square(8537341, 50) is:

2921.8728582879851242173838229735693053765773170487

which is only by one last digit of from python's

getcontext().prec = 50
print str(Decimal(8537341).sqrt())

2921.8728582879851242173838229735693053765773170488

This one digit is off because the last digit is not really precise.

As always Go Playground.

P.S. if someone would find a native way to do this, I would gladly give my accept and upvote.

查看更多
叼着烟拽天下
3楼-- · 2019-07-21 15:50

Adding precision

There is probably a solution in go but as I don't code in go, here is a general solution.

For instance if your selected language doesn't provide a solution to handle the precision of floats (already happened to me):

If your language provides you N digits after the dot, you can, in the case of the square root, multiply the input, here 2, by 10^(2*number_of_extra_digits).

For instance if go would give you only 1.41 as an answer but you would want 1.4142, then you ask it the square root of 2*10^(2*2) = 2*10000 instead and you get 141.42 as an answer. Now I leave it up to you to rectify the placement of the dot.

Explanation: There is some math magic behind it.

If you were to want to add some precision to a simple division, you would just need to multiply the input by 10^number_of_extra_digits.

The trick is to multiply the input to get more precision as we can't multiply the output (the lost of precision already happened). It does work because most languages cut more decimals after the dot, than before it.

So we just need to change the output equation to the input equation (when possible):

For simple division: (a/b) * 10 = (a*10)/b

For square root: sqrt(a) * 10 = sqrt(a) * sqrt(100) = sqrt(a*100)

Reducing precision

Some similar tinkering can also help to reduce the precision if needed.

For instance if you were trying to calculate the progression of a download in percent, two digits after the dot.

Let's say we downloaded 1 file on 3, then 1/3 * 100 would give us 33.33333333.

If there is no way to control the precision of this float, then you could do cast_to_an_int(1/3 * 100 * 100) / 100 to return 33.33.

查看更多
登录 后发表回答