V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
wangbenjun5
V2EX  ›  程序员

关于 golang 除法运算符/的疑惑

  •  
  •   wangbenjun5 · 2020-06-29 17:47:41 +08:00 via Android · 6259 次点击
    这是一个创建于 1608 天前的主题,其中的信息可能已经有所发展或是发生改变。
    无意间发现的一个问题,举个栗子:

    10/4 = 2
    10/4.0 = 2.5
    10.0/4 = 2.5

    上面这个 2 个都可以理解,和大 PHP 一样,只要有一方是浮点型结果就是浮点型。

    重点来了,如果被除的数不是字面量值,而是一个变量,比如

    var ten = 10
    ten/4.0 = 2

    这个结果就有点奇怪了,有人说这是因为在 golang 里面结果取决于被除数。但是为毛使用字面量的时候就不一样了呢?
    11 条回复    2020-06-30 15:31:30 +08:00
    XanderChen
        1
    XanderChen  
       2020-06-29 18:02:33 +08:00
    我怎么记得不加类型注解要用 := 来声明变量来着。

    另外你把 10 改成 10.0 试试,没准是取决于...emmm....
    因为 10 被隐式声明为整型,你又没声明你需要的数据类型,所以编译器决定给你整型结果?

    要不你把 4.0 也做个变量再除一下试试。
    rrfeng
        2
    rrfeng  
       2020-06-29 18:04:29 +08:00
    字面量会进行类型推断,变量不需要类型推断,算式中的字面量直接按变量类型来,否则就得做强制类型转换。

    大概就是这么回事。
    nidaye999
        3
    nidaye999  
       2020-06-29 18:09:22 +08:00
    浮点数不能给整数运算。
    lxy42
        4
    lxy42  
       2020-06-29 18:13:02 +08:00
    你试试 ten / 4.1 大概就知道了
    si
        5
    si  
       2020-06-29 18:15:51 +08:00 via Android
    这种问题直接贴测试代码,大佬一看就清楚了。
    wangbenjun5
        6
    wangbenjun5  
    OP
       2020-06-29 18:28:01 +08:00 via Android
    我好奇的是为什么这么设计,前后有点矛盾,如果说取决于被除数那 10/4.0 也应该是 2,而不是 2.5 。

    唯一能解释通的是,编译器做了类型推断,因为除数是浮点型,所有结果也是浮点数。
    secondwtq
        7
    secondwtq  
       2020-06-29 19:03:45 +08:00
    随手看了一下 spec,你这个事 constant expression,有这么一段:

    > Any other operation on untyped constants results in an untyped constant of the same kind; that is, a boolean, integer, floating-point, complex, or string constant. If the untyped operands of a binary operation (other than a shift) are of different kinds, the result is of the operand's kind that appears later in this list: integer, rune, floating-point, complex. For example, an untyped integer constant divided by an untyped complex constant yields an untyped complex constant.

    大概是为了 大道至简 把 C 里面的类型提升给整过来了。
    favourstreet
        8
    favourstreet  
       2020-06-29 19:12:40 +08:00
    @secondwtq 我觉得是反了,压根没整过来才出这种问题。Numeric types 一节里有这样一句话:
    > Explicit conversions are required when different numeric types are mixed in an expression or assignment.
    意思是必须手动转换类型,所以 C 里面行为明确的类型提升,go 里面变成未定义行为了……,而且按照常量表达式里的那句话,2.5/5 应该等于 0,实际上等于 0.5,这算什么?

    所以一个语言有个 ISO 标准的好处就在这里……
    secondwtq
        9
    secondwtq  
       2020-06-29 19:29:15 +08:00
    @favourstreet 我觉得设计者的意思是,为了避免类型提升可能存在的问题,于是甩掉了 general 的类型提升,然后为了一点小方便,在 constant expression 这个 special case 里面允许这么做。可能是以为这样不仅 大道至简,还能避免类型提升可能的问题吧(至于类型提升这个东西自身到底应不应该存在?这个问题我推测他们并没有想过)。
    结果就出了楼主这个问题。

    > 而且按照常量表达式里的那句话,2.5/5 应该等于 0,实际上等于 0.5,这算什么?
    我不知道你是怎么看出来的,他还举了例子,integer constant / complex constant = complex constant 。
    favourstreet
        10
    favourstreet  
       2020-06-29 19:49:35 +08:00
    @secondwtq 我确实饿极了脑子停转弄错了……我没用过 go 所以去官网上玩了一下 Try Go 发现能重现楼主提到的问题。我另外发现,不同类型变量之间的运算会报编译错误,根据语法还有这些错误提供的线索,我猜编译器可能是这样想的:
    ten/4.0 这个表达式里,4.0 是一个常量,神奇的是,常量可以没有类型( untyped )!于是编译器认为转换成整型没有损失信息,还免得编译失败,于是开开心心转成整形了。
    katsusan
        11
    katsusan  
       2020-06-30 15:31:30 +08:00
    golang 的编译大概分为三步,
    #1.进行词法分析和语法分析,构建出语法树 AST
    #2.进行类型检测 typecheck 和 AST 优化(var x = 10/4.0 就在这步计算出来)
    #3.进行静态单赋值 SSA 优化(ten/4.0 在这步计算得出)和中间代码生成

    解析 10/4.0 时,经过一系列 switch case 后会调用[evconst]( https://github.com/golang/go/blob/master/src/cmd/compile/internal/gc/const.go#L569)计算出 10/4.0 写到 x 节点上,而 evaconst 会先调用[match 函数]( https://github.com/golang/go/blob/master/src/cmd/compile/internal/gc/const.go#L686)
    来将两个变量转化为同样类型,这个函数逻辑对应着#7 所记录的 spec 规范。

    而碰到 print(ten/4.0)时由于左边为变量已经推导出为 int 型所以会被[defaultlit2 函数]( https://github.com/golang/go/blob/master/src/cmd/compile/internal/gc/const.go#L1126)获取并且按左节点优先原则将右边的 4.0 转化为左边的类型。

    可以从 GOSSAFUNC=main go build x.go 获得的 html 里看到 main 函数从 source->AST->SSA 的转换过程。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2691 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 05:31 · PVG 13:31 · LAX 21:31 · JFK 00:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.