有常识的人都知道,在除法运算中不能除以零,而我们在实际的应用中面对大量的上下文,很有可能因为考虑不周就出现除以零的情况。因此,我们有必要知道除以零到底会不会panic?如果不发生panic,又会得到什么样的值?

下面,我们带着这样的疑问继续阅读本文。相信在读完本文后,这两个疑问会烟消云散。同时,为了能够让读者快速地了解本文的全貌,下面列出本文的大纲。

除以零值

在Go中,可能除以零的情况分为三种,分别是除以常量0、整形0和浮点数0。下面我们分别看一下这三种情况的实际表现。

除以常量0

根据上图知道,除以常量0是无法编译通过的。这一点,还是比较令人安心。

除以整形0

根据上图知,除以整形0会发生panic。这一点,在平时的开发中还需格外注意。

除以浮点数0

除以浮点数0,情况会略微复杂。请看下代码和输出结果。

var zero = float64(0)
fmt.Println(1024 / zero) // 输出:+Inf
fmt.Println(-1024 / zero) // 输出:-Inf
fmt.Println(0 / zero) // NaN

上面输出中Inf为单词infinity的缩写,该单词含义为无穷,因此+Inf-Inf分别表示正无穷和负无穷。

NaN意味着not a number,即结果不是一个数。

到这里,老许不得不感叹浮点数确实博大精深,在Go里面除以0确实不会panic(经过老许验证,在python里面会发生错误)。另外,上述中0/zero得到NaN,而整形中0除以0依旧会panic。

±Inf值

判断是否是±Inf

前面通过正数和负数分别除以浮点数0可到正无穷和负无穷。Go里面math包提供的Inf函数也可以得到正无穷和负无穷,同时还提供了IsInf函数用于判断是正无穷还是负无穷。

math.Inf函数签名为func(int) float64,当传入的参数大于等于0时返回正无穷,否则返回负无穷。

math.IsInf函数签名为func(float64, int) bool,第一个参数为待判断的值,第二个参数大于0时,返回第一个参数是否为正无穷,第二个参数小于0时,返回第一个参数是否为负无穷,第二个参数等于0时,返回第一个参数是否为无穷。

具体验证,请看下面代码和输出。

positiveInf := math.Inf(1)
negativeInf := math.Inf(-1)
// 判断是否为正无穷
fmt.Println(math.IsInf(positiveInf, 1)) // 输出:true
// 判断是否为负无穷
fmt.Println(math.IsInf(negativeInf, 1)) // 输出:false
// 判断是否为正无穷
fmt.Println(math.IsInf(positiveInf, -1)) // 输出:false
// 判断是否为负无穷
fmt.Println(math.IsInf(negativeInf, -1)) // 输出:true
// 判断是否为无穷
fmt.Println(math.IsInf(positiveInf, 0)) // 输出:true
// 判断是否为无穷
fmt.Println(math.IsInf(negativeInf, 0)) // 输出:true
// 判断是否为无穷
fmt.Println(math.IsInf(1024, 0)) // 输出:false

±Inf的比较

正无穷和负无穷输出结果和我们平时看到数值类型迥然不同,而且也无法直接将±Inf直接赋值给一个浮点类型的变量,那么他们是否可以参与数值之间的比较呢,请看下面代码和输出。

positiveInf := math.Inf(1)
negativeInf := math.Inf(-1)
// 判断正无穷是否可以和自身比较
fmt.Println(positiveInf == positiveInf) // 输出:true
// 判断负无穷是否可以和自身比较
fmt.Println(negativeInf == negativeInf) // 输出:true
// 判断正无穷是否大于正数
fmt.Println(positiveInf > math.MaxFloat64) // 输出:true
// 判断负无穷是否小于正数
fmt.Println(negativeInf < -math.MaxFloat64) // 输出:true

根据输出结果知,正负无穷可以比较,且表示的值确实暗合无穷这一数学意义。

NaN值

判断是否是NaN

在这里加深一下生成NaN值的印象,浮点数零相除即可得到NaN值。同样,math包提供了NaN函数和IsNaN函数分别用于返回一个NaN值和判断一个浮点数是否为NaN

请看下面代码和输出。

nan := math.NaN()
fmt.Println(math.IsNaN(nan))  // 输出:true
fmt.Println(math.IsNaN(10.2)) // 输出:false

其他可生成NaN值的情况:

  1. 任何与NaN一起的运算结果都为NaN
  2. 如果x < -1 || x > 1,则math.Acos(x)math.Asin(x)math.Atanh(x)均返回NaN
  3. 如果x < 1,则math.Acosh(x)返回NaN
  4. 如果x < 0,则math.Sqrt(x)返回NaN

应该还有其他返回NaN的情况,老许就不一一总结了。

NaN的比较

根据静态检查的提示“no value is equal to NaN, not even NaN itself”,没有值等于NaN甚至于它自己都不是它自己。为了做更进一步的验证,我们看下面代码和输出。

nan := math.NaN()
positiveInf := math.Inf(1)
negativeInf := math.Inf(-1)
fmt.Println(nan == nan)

fmt.Println(nan > 1024) // 输出:false
fmt.Println(nan < 1024) // 输出:false
fmt.Println(nan == 1024) // 输出:false
fmt.Println(nan > positiveInf) // 输出:false
fmt.Println(nan < negativeInf) // 输出:false
fmt.Println(nan == positiveInf) // 输出:false
fmt.Println(nan == negativeInf) // 输出:false

确实,如提示所说,NaN不等于它自己,且它和任何值比较时都为false。由于NaN的特殊性,所以在实际开发中我们一定要注意边界值的处理。

写在最后

本文主要讲解了除以常量0、整形0和浮点数0的各种情况,以及对±InfNaN的可比性做了分析。下面是一张各种值是否可比的对照表。

是否可比 +Inf -Inf NaN Number
+Inf Y Y N Y
-Inf Y Y N Y
Nan N N N N
Number Y Y N Y

这里说的不可比主要指NaN和任何值比较都返回false

最后,衷心希望本文能够对各位读者有一定的帮助。

【关注公众号】