在进行
big.Int
类型的简单相互赋值过程中发生了浅拷贝,
big.Int
类型数据存储的实体
[]uint
并未发生变更,导致出现数据紊乱。
在Golang中,标准库提供了big包用来进行大数运算。为了研究它的用法,我编写了下边这个小程序来验证它的特性。程序的逻辑很简单,初始化两个大数变量
a = 1
和
b = 2
,然后使用中间变量法对
a
和
b
进行交换,交换完毕后再对中间变量加100,然后输出交换的结果。
这个程序足够简单,以至于简单到我们可以立马说出它的答案,
a = 2, b = 1
但是当控制台中结果输出的那一刻发现事情并不简单。。。
package main
import (
"fmt"
"math/big"
func main() {
// 初始化两个变量: a = 1, b = 2
a := big.NewInt(1)
b := big.NewInt(2)
// 打印交换前的数值
fmt.Printf("a = %v b = %v\n", a, b)
// 使用中间变量法进行交换
tmp := a
a = b
b = tmp
// 交换完成, 对中间变量加100
tmp.Add(tmp, big.NewInt(100))
// 打印交换后的结果
fmt.Printf("a = %v b = %v tmp = %v\n", a, b, tmp)
a = 1 b = 2
a = 2 b = 101 tmp = 101
从结果中可以看出,a和b的内容确实进行了交换,但是对中间变量tmp加100的操作貌似也对变量b生效了。这是为什么呢?
在上述程序中,我们使用 big.NewInt()
函数对 a、b
进行了初始化,为此,我们找到该函数的实现:
// NewInt allocates and returns a new Int set to x.
func NewInt(x int64) *Int {
return new(Int).SetInt64(x)
从实现上可以看出,该函数会返回一个 *Int
类型,也就是类型 Int
的指针。回头看我们的程序,对于 tmp := a
这条语句而言,实际上是将 a
所指向的地址存进了 tmp
变量中,后续对于 tmp
变量的一切操作实则就是对 a
变量的操作,所以 tmp.Add(tmp, big.NewInt(100))
这条语句也对 b(交换前的a)
变量生效。
问题似乎得到了解决,接下来更改程序,验证猜想:
package main
import (
"fmt"
"math/big"
func main() {
// 初始化两个变量: a = 1, b = 2
a := big.NewInt(1)
b := big.NewInt(2)
// 打印交换前的数值
fmt.Printf("a = %v b = %v\n", a, b)
// 使用中间变量法进行交换
tmp := *a
*a = *b
*b = tmp
// 交换完成, 对中间变量加100
tmp.Add(&tmp, big.NewInt(100))
// 打印交换后的结果
fmt.Printf("a = %v b = %v tmp = %v\n", a, b, tmp)
在第二版程序中,对原来交换逻辑做了更改,将指针的赋值操作更改为对指针的指向做取值操作。
a = 1 b = 2
a = 2 b = 101 tmp = 101
然鹅。。。从输出结果来看,问题并没有得到解决。
为什么取值操作没有什么卵用呢?我们注意到这里的 a
、b
、tmp
实际上是标准库中的 Int
类型,而非保留字 int
类型,所以是不是 Int
类型的实现导致我们的赋值操作无效呢?
// An Int represents a signed multi-precision integer.
// The zero value for an Int represents the value 0.
type Int struct {
neg bool // sign
abs nat // absolute value of the integer
// An unsigned integer x of the form
// x = x[n-1]*_B^(n-1) + x[n-2]*_B^(n-2) + ... + x[1]*_B + x[0]
// with 0 <= x[i] < _B and 0 <= i < n is stored in a slice of length n,
// with the digits x[i] as the slice elements.
// A number is normalized if the slice contains no leading 0 digits.
// During arithmetic operations, denormalized values may occur but are
// always normalized before returning the final result. The normalized
// representation of 0 is the empty or nil slice (length = 0).
type nat []Word
// A Word represents a single digit of a multi-precision unsigned integer.
type Word uint
如上,是标准库中 Int
类型的定义。从定义中可以看出,该struct共有两个成员变量:一个表示当前是否为负数的 neg
变量,一个表示当前数值绝对值的 abs
变量,而 abs
变量的类型是 nas
类型,该类型实际上是一个 []uint
类型,即uint
的splice。在golang中,splice是一种引用类型,即对于splice类型进行的参数传递、赋值等操作实际上是对其引用的赋值以及传递。所以,在我们第二版程序中的赋值操作其实是执行了一次浅拷贝,即将成员变量 abs
的引用更改为了 a
变量的引用,当 a
和 b
交换后 tmp
的成员变量 abs
依然执行交换前 a
变量的 abs
地址,所以 tmp
变量的变更其实还是对交换前 a
变量的变更。
知道了原因,解决这个问题的方案就有了,我们只需要使用 big.Int 提供的 Set()
方法,对其进行深拷贝即可。当然,在数据量比较大的时候,深拷贝固然安全,但是其性能消耗也是蛮大的,具体使用哪种方式还是需要读者根据实际使用场景进行决断。
package main
import (
"fmt"
"math/big"
func main() {
// 初始化两个变量: a = 1, b = 2
a := big.NewInt(1)
b := big.NewInt(2)
// 打印交换前的数值
fmt.Printf("a = %v b = %v\n", a, b)
// 使用中间变量法进行交换
tmp := big.NewInt(0)
tmp.Set(a)
a.Set(b)
b.Set(tmp)
// 交换完成, 对中间变量加100
tmp.Add(tmp, big.NewInt(100))
// 打印交换后的结果
fmt.Printf("a = %v b = %v tmp = %v\n", a, b, tmp)
a = 1 b = 2
a = 2 b = 1 tmp = 101
结论先行在进行 big.Int 类型的简单相互赋值过程中发生了浅拷贝,big.Int 类型数据存储的实体 []uint 并未发生变更,导致出现数据紊乱。问题引入在Golang中,标准库提供了big包用来进行大数运算。为了研究它的用法,我编写了下边这个小程序来验证它的特性。程序的逻辑很简单,初始化两个大数变量 a = 1 和 b = 2,然后使用中间变量法对 a 和 b 进行交换,交换完毕后再...
Big是golang内置的*big.Float类型的简单,不可改变的包装器,其目的是提供更用户友好的API和不变性保证,但要以牺牲一些运行时性能为代价。
用法很简单:
dec := big . NewDecimal ( 1.24 )
addend := big . NewDecimal ( 3.14 )
dec . Add ( addend ). String () // prints "4.38"
Go练习(大数)
Go练习,试试go的大数类,bigInt就不分析了,分析的人很多(主要还是我菜)…具体讲讲怎么用
找了道题试试(洛谷p1009),求前n项的阶乘和(n<=50)
初始化一下大数数组,弄个大数的指针c记录计算结果,res用来计算就好了
package main
import (
"fmt"
"math/big" //大数类
var n int64
func main() {
fmt.Scan(&n)
a:=make([]big.Int,55) //初
math/big 作为 Go 语言提供的进行大数操作的官方库,在以太坊 Ethereum 项目中作为 currency 的类型表示得到了广泛的使用,这篇文章主要介绍该库的使用。
官方包解析
在官方的 math/big 包中,Int 类型定义如下:
// An Int represents a signed multi-precision integer.
// The zero v...
go语言中内置了
big类型的数据,其包含很多常用的方法,比如比较两个
大数是否相等,cmp,如果是-1表示前面的数字比较小,0表示相等,1表示前面的数字比较大。
new
Int可以对数据进行初始化,
可以轻松从其他的类型的数据得到
big类型的数据。
big在椭圆曲线加密中经常会用到。
nil 是 interface、function、pointer、map、slice 和 channel 类型变量的默认初始值
不可以用"=="比较类型: slice(切片)、map(key-value集合)、func(函数)
map的比较可以使用reflect.DeepEqual()
m1 := map[string]string{"one": "a", "two": "b"}
m2 := map[string]string{"two": "b", "one": "a"}
// 1.map类型
在进行以太币转账之前,需要比较big.Int格式的余额bigWeiBalance和转账金额bigWeiValue。遇到一个很傻的问题就是big.Int 没有 "<"">"的比较。
比较方法如下:
enough := bigWeiBalance.Cmp(bigWeiValue)
原来,big.Int 类自带cmp方法
返回 1:前面的big.Int 实例大于cmp方法big.I...
f, bool := new(
big.Float).SetString("100.02222")
if bool == false {
log.Error("err:SendTxAcceptRecord")
//正常转
大数
s1, boolSet := new(
big.Float).SetString("1000000000...
net.Conn.Read 方法是 Go 语言标准库中 net.Conn 接口的一部分,用于从连接读取数据。它接受一个字节切片作为参数,并将连接中的数据读入该切片,返回实际读取的字节数和可能的错误。
使用方法:
n, err := conn.Read(buf)
参数 buf 是存储读取数据的字节切片,n 是实际读取的字节数,err 是可能的错误。
如果在读取时遇到错误或连接已关闭,则 err 会返回 io.EOF 值。