math/bigint clone

3,518 views
Skip to first unread message

johnwilli...@gmail.com

unread,
Mar 21, 2016, 7:21:34 PM3/21/16
to golang-nuts


Hi,

I expect the following to give me a copy of a bigint, but I only get a copy of a reference:

        a := big.NewInt(87)
b := big.NewInt(0)
*b = *a
fmt.Println("a", a, "b", b)
b = b.Sub(b,big.NewInt(1))
fmt.Println("a", a, "b", b)

In this case, I would expect a to remain unchanged by the Sub() invocation on b. But both a and b are decremented and output is,

a 87 b 87
a 86 b 86

I get the intended result with SetString():

        c := big.NewInt(87)
	d := big.NewInt(0)
	
	d.SetString(c.String(),10)
	d = d.Sub(d,big.NewInt(1))
	fmt.Println("c", c, "d", d)

Output:
c 87 d 86

Is SetString() idiomatic for cloning?

Thanks
https://play.golang.org/p/-kT3Pc-jhb




xingtao zhao

unread,
Mar 21, 2016, 7:43:17 PM3/21/16
to golang-nuts, johnwilli...@gmail.com
To clone, you may:

b := new(big.Int)
b = b.Set(a)

or

b := new(big.Int).Set(a)

johnwilli...@gmail.com

unread,
Mar 21, 2016, 7:47:15 PM3/21/16
to golang-nuts

Thanks Xingtao! 

Francis

unread,
Mar 22, 2016, 6:37:17 PM3/22/16
to golang-nuts, johnwilli...@gmail.com
Xingtao's advice is right. But it's probably worth looking closely at what you were actually getting from the line 

    *b = *a

You weren't getting a copy of a reference, you were copying the values from one big.Int to another. The tricky part is that big.Int contains a reference internally.

type Int struct {
    		neg bool // sign
    		abs nat  // absolute value of the integer
}
The subtle field here is nat, which is defined as
type nat []Word
While Word is defined as
type Word uintptr
So we can go back and clarify the Int struct to 
type Int struct {
    		neg bool // sign
    		abs []uintptr
}
We can test that strict values were copied by just changing the neg value in one of them.
https://play.golang.org/p/7FlMtfjfgY
Now we can see that those two big.Ints are pointers to different locations in memory. But they both happen to point to the same []uintptr.
What other behaviour can we get out of this situation.
https://play.golang.org/p/-5Nxz3tJnr
If we modify b using setString(...) we can see that a is also modified. But in a very odd way. I am not sure exactly what is happening here, but I would expect that b's value is built up step by step inside its []uintptr slice internally until that slice is found to be too short and a new slice is allocated to store the really big number we put in that string. But while we built up b's value we were also modifying a's []uintptr, leaving garbage inside a.
Copying values using the *a = *b is really useful. But a rule is that you only ever do it when you know exactly what you are copying. There is probably a stronger rule of thumb here that you shouldn't copy like this between types that you don't control (i.e. you wrote them yourself with this use in mind).
F
I have no idea what happened to the formatting of this email. After I copy and pasted from golang.org everything just got out of hand.
If anyone can clarify why big uses uintptr for nat I would be interested to know. To me it seemed unexpected, but looks so deliberate there must be a good (and interesting) reason.

:)
On Tuesday, 22 March 2016 00:47:15 UTC+1, johnwilli...@gmail.com wrote:

Thanks Xingtao! 

johnwilli...@gmail.com

unread,
Mar 23, 2016, 1:21:23 AM3/23/16
to golang-nuts, johnwilli...@gmail.com


Francis,

I agree with your analysis. I should have been more precise. The *a = *b is a shallow copy and, as you say, this could be a risky idiom for pointers in general.

As to why bigint uses []uninptr, and I am speculating ,this is an efficient way to store numeric content. You can see this if you print both the reference and the value:


Oddly enough, your formatting was okay on my phone. In the browser, however, there are gaps.

Thanks for looking into this.  :)

Francis

unread,
Mar 23, 2016, 8:57:57 AM3/23/16
to golang-nuts, johnwilli...@gmail.com
No worries, it was quite fun :)
Reply all
Reply to author
Forward
0 new messages