Golang中为什么3*(1/3)不等于3*1/3

Golang中为什么3*(1/3)不等于3*1/3 大家好,请允许我简单介绍一下自己,我是柏林一所文理中学的老师,正在为我的信息学课程准备一些Go语言的练习。我的主要教学科目是音乐和德语。这是我在这里的第一次发帖。

以下代码:

VAR result float64
result=3*(1/3)
fmt.Printf("Result is %.2f", result)

输出结果为 0

当我将计算式改为 3*1/3(去掉圆括号)时,它正确地输出了 1。

有人能向我解释一下这种行为吗?这与Go编译器的工作方式有关,还是数据类型的问题?

此致!

3 回复

简而言之: 3*(1/3) 仅由整数组成,因此计算也将使用整数进行。考虑到这一点,1/3 等于 0,这使得结果为零。 你可以使用 3*(1.0/3),这样就没问题了。

func main() {
    fmt.Println("hello world")
}

更多关于Golang中为什么3*(1/3)不等于3*1/3的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


基本上,问题在于:

  • 在Go(以及我所知道的所有其他类C语言)中,数字具有类型。
  • Go(以及我所知道的所有其他类C语言)会对每个表达式进行求值。

Go中的数字类型:

在Go中,数字可以是整数、浮点数或复数。构成你的 3*(1/3) 表达式的 313 都是整数,因此适用整数运算规则:

当你写下 3*(1/3) 时,Go理解运算符优先级,知道必须先计算 1/3。当处理整数时,1 / 3 = 0,余数为1。Go不保留余数,所以你只剩下0。3 * 0 = 0。

当你去掉括号时,因为除法和乘法具有相同的优先级和结合性,Go会先计算 3 * 1 = 3,然后计算 3 / 3 = 1,余数为0,所以结果是1。

表达式求值

当我们看到像 1/3 这样的表达式时,我们通常有一种直觉,觉得可能应该把这个数字保留为分数形式。大多数用Go编程的人生活在以十进制为常态的社会中。我们知道1/3不能很好地转换为十进制。如果你将每个表达式四舍五入到十分位,那么 1 / 3 = 0.3,然后 0.3 * 3 得到 0.9,而不是 1.0。当我们在脑海中计算表达式 3 * (1/3) 时,我们在概念上把 1/3 保留为分数,而不是计算它,然后乘以 3 得到 3/3 = 1。Go(以及我所知道的所有其他类C语言)并不“知道”这一点,因此那些不能很好地适应计算机二进制系统的表达式仍然会被求值。

一种解决方法是使用 math/big 包。该包定义了一个 big.Rat 类型(意为“大有理数”),可以表示像 1/3 这样的比例。与直接写出运算符相比,它的API有些笨拙,但它的设计旨在处理任意精度有理数时既准确又快速。

编辑:有人提出了一个很好的观点,如果你将 1/3 中的 1 或 3 改为 1.0 或 3.0,那么 1/3 的求值将变成 float64 类型,你又会得到正确的答案。

在Go语言中,这种行为与整数除法的截断特性有关,而不是编译器的问题。让我们分析一下:

package main

import "fmt"

func main() {
    // 情况1:使用括号
    var result1 float64
    result1 = 3 * (1 / 3)  // 问题在这里
    fmt.Printf("3 * (1/3) = %.2f\n", result1)  // 输出: 0.00
    
    // 情况2:不使用括号
    var result2 float64
    result2 = 3 * 1 / 3
    fmt.Printf("3 * 1 / 3 = %.2f\n", result2)  // 输出: 1.00
    
    // 让我们分解第一个情况
    fmt.Println("\n分解计算过程:")
    
    // 1/3 是整数除法
    integerDivision := 1 / 3
    fmt.Printf("1 / 3 (整数除法) = %d\n", integerDivision)  // 输出: 0
    
    // 3 * 0
    multiplication := 3 * integerDivision
    fmt.Printf("3 * %d = %d\n", integerDivision, multiplication)  // 输出: 0
    
    // 转换为float64
    finalResult := float64(multiplication)
    fmt.Printf("转换为float64: %.2f\n", finalResult)  // 输出: 0.00
}

关键点在于 1/3 是整数除法。在Go中:

  • 13 都是整数常量
  • 整数除法会截断小数部分,所以 1/3 = 0
  • 3 * 0 = 0

要得到正确结果,有几种方法:

package main

import "fmt"

func main() {
    // 方法1:使用浮点数常量
    result1 := 3 * (1.0 / 3.0)
    fmt.Printf("使用浮点数: 3 * (1.0/3.0) = %.2f\n", result1)
    
    // 方法2:显式类型转换
    result2 := 3 * (float64(1) / float64(3))
    fmt.Printf("类型转换: 3 * (float64(1)/float64(3)) = %.2f\n", result2)
    
    // 方法3:使用浮点数变量
    var a, b float64 = 1, 3
    result3 := 3 * (a / b)
    fmt.Printf("使用变量: 3 * (a/b) = %.2f\n", result3)
}

3*1/3 的情况下,计算从左到右进行:

  • 3 * 1 = 3 (整数乘法)
  • 3 / 3 = 1 (整数除法)

所以结果是正确的。这与运算符优先级和整数除法的截断特性有关。

回到顶部