Go无符号整数运算时反转问题

Go无符号整数运算时反转问题

虽然看过go安全开发相关文档,但是在写代码时,仍然会不小心忽略一些问题,比如go的整数安全——无符号整数运算时反转问题。

出现原因

在一个直播后台项目中,需要统计时间段内主播的打赏星辰变化,比如:前一天主播获得总星辰是200,今天总星辰是100,获取比前一天的提升或下降百分比,就是(100-200)/200 ,下降50% 。当时想着打赏不会出现负数,所有设置的是 uint64类型,但是统计变化率就要有减运算,就会出现负数,出现了很大一个bug,结果就是星辰提升百分比 18446744073709551615,很大一个结果,当时惊呆了,就查看什么原因…

先看代码

func main() {
	
    var a, b uint = 1, 2
	var c, d uint = 100, 200
	fmt.Println("a-b=", a-b)  // -1
	fmt.Println("c-d=", c-d)  // -100
	// 结果:
	//a-b= 18446744073709551615
	//c-d= 18446744073709551516
}

分析原因

根本原因:
1, go 中有符号整数和无符号整数保存的二进制不一样。
2, go 中正数用二进制原码表示,负数用补码表示。

1, 先理解 什么是原码、补码?
  1. 二进制的最高位是符号位:0表示正数,1表示负数。
  2. 正数的原码、反码、补码都一样。
  3. 负数的补码 是它的反码+1, 即原码符号位不变,其它位都取反,再加1 。
  4. 0的反码、补码都是0 。

以 int8 和 uint8 为例看代码:

var x, y int8 = 3, -3

x 是正数,原码就是二进制: 0000 0011
y 是负数,用补码表示 
y 的原码:1000 0011
y 的反码:1111 1100  
y 的补码:1111 1101

实际保存的x,y 分别是 0000 00111111 1101 .

因为 int8 二进制最高位用来表示符号位,所有范围是 -128 ~ 127 .uint8 已经表示无符号,最高位就是二进制数,范围是 0255 .

2, 分析 uint中 -1变成 18446744073709551615 原因
uint 类型的实际大小和计算机CPU有关,如果是64位,那么就和 uint64一样大,32位就是uint32一样大。

最上面代码变量a、b 都是 uint类型,就是 uint64 类型
a-b的结果是 -1 
得出-1后会先按负数来解析二进制
原码:1000 0000 ... 0000 0001
反码:1111 1111 ... 1111 1110
补码:1111 1111 ... 1111 1111

-1保存的二进制数是641,就是uint64的最大数,
因为是无符号类型uint,所以输出结果时不会再按负数解析结果,
输出结果就是uint64的最大数18446744073709551615

你可能感兴趣的:(Golang,golang,go安全,整数安全,无符号反转,补码)