go 杂项

go 杂项

2023/04/15

http.Response.Body

注意 Body 字段的注释:

The default HTTP client’s Transport may not reuse HTTP/1.x “keep-alive” TCP connections if the Body is not read to completion and closed.

为了连接复用,需要读取和关闭 body,如果实际不用 body 时可使用 io.Copy(io.Discard, resp.Body)

使用 strace 查看连接情况: strace -qqfe connect ./read_body 2>&1 | grep -E '\(80\)|doRequest'

json.Marshal

结构体内部匿名字段实现了 json.Marshaler(方法: MarshalJSON() ([]byte, error)), 则会直接调此 MarshalJSON 而忽略结构体. 源码:

  1. 创建序列化 encoderFunc: f = newTypeEncoder(t, true)
  2. 判断是否实现了 json.Marshaler if t.Implements(marshalerType) { return marshalerEncoder }

所以结构体内匿名字段要注意, 如下面 FileWrapper 的序列化会被 File 顶替.

json.Unmarshal 解析数值到 interface

数值类型序列化到 interface, 会默认当作 float64 来解析, 源码:

  1. case reflect.Interface: n, err := d.convertNumber(string(item))
  2. strconv.ParseFloat(s, 64)

所以遇到长整型反序列化成 interface 时要注意精度. 小数部分超过 53 位则有可能丢失精度.

IEEE 754 64-bit floating-point numbers

format.svg
format.svg

公式是: $(-1)^{\text {sign }}\left(1 . b_{51} b_{50} \ldots b_{0}\right)_{2} \times 2^{e-1023}$, 即先将数字转成二进制, (-1)^sign * 1.b_xx * 2^E.

十进制小数表示流程: 2.25 转二进制过程: 整数部分是 2, 通过"除2取余数"得到二进制 10, 小数部分是 0.25, 通过"乘2取整数"得到二进制 01, 所以 $2.25$ 的二进制是 $10.01$, 科学计数法表示为 $1.001 × 2^1$,

  • 符号位: 是正数所以是 0
  • 指数部分: $1.001 × 2^1$ 得到指数是 1, 则存储的指数是 $1 + 1023 = 1024$, 二进制是 10000000000
  • 小数部分: $1.001 × 2^1$ 得到小数是 001, 则存储的小数是 001 右侧补 0 直到长度为 52

可以看到 2 ^ 53 + i (二进制表示为 $1.\overbrace{0\cdot\cdot\cdot \text{X}1 }^{53\text{位}}$) 当 i=1,3,5,… 时会丢失精度, 原因是这些数的小数部分二进制表示是 0...X[1] 共计 53 位取有效的前 52 位, 而被丢弃的最后一位是 1 不是 0.

Last updated on