1

I found some odd looking code in the math/big library and don't understand how it works. I have extracted the constants out into an example:

package main

import (
    "fmt"
    "math/bits"
)

const (
    _S = _W / 8 // word size in bytes

    _W = bits.UintSize // word size in bits
    _B = 1 << _W       // digit base
    _M = _B - 1        // digit mask
)

func main() {
    fmt.Println(_S, _W, _B, _M)
}

Returns the error:

bug/example.go:17:13: constant 18446744073709551616 overflows int
bug/example.go:17:13: constant 18446744073709551615 overflows int

To be clear, I understand why this code doesn't work. What I'm interested in is knowing is why this error doesn't happen when this constant is used in the standard library?

I thought it might be late evaluated (ie _B - 1) but I can see it's literally used in tests. So what value would be used here?

Elliot Chance
  • 5,526
  • 10
  • 49
  • 80

1 Answers1

0

"constant 18446744073709551616 overflows int?" -- Yes.

int depending on the architecture is at least 32 bits. int32 max value is 2147483647 and int64 max value is 9223372036854775807 both of which are not large enough to hold 18446744073709551615.


https://golang.org/ref/spec#Constants

Numeric constants represent exact values of arbitrary precision and do not overflow. Consequently, there are no constants denoting the IEEE-754 negative zero, infinity, and not-a-number values.

A constant may be given a type explicitly by a constant declaration or conversion, or implicitly when used in a variable declaration or an assignment or as an operand in an expression. It is an error if the constant value cannot be represented as a value of the respective type.

In your case, based on the expression in which it is used, the constant value is given the type int, and since it cannot be represented by that type it results in an error.


You could convert the constants to float64 (or uint in the case of _M) if you want the code to compile.

package main

import (
    "fmt"
    "math/bits"
)

const (
    _S = _W / 8 // word size in bytes

    _W = bits.UintSize // word size in bits
    _B = 1 << _W       // digit base
    _M = _B - 1        // digit mask
)

func main() {
    fmt.Println(_S, _W, float64(_B), uint(_M))
}

https://play.golang.org/p/AVT5FPHinT3


For more see: Does Go compiler's evaluation differ for constant expression and other expression

mkopriva
  • 35,176
  • 4
  • 57
  • 71
  • Thanks, I understand why it doesn't work. What I'm interested in is how that code in the standard library doesn't error in the same way? – Elliot Chance Jul 11 '21 at 14:17
  • 2
    @ElliotChance `math/big` doesn't call `Printf` with it. It does use it to initialize a `uint`, but the value isn't too big for a `uint` :) – hobbs Jul 11 '21 at 14:28
  • 2
    _"You could convert the constants to `float64`"_ — this will lose precision. – Ruslan Jul 11 '21 at 14:31
  • @Ruslan that, I presumed, to be self evident. – mkopriva Jul 11 '21 at 14:32
  • 2
    *"What I'm interested in is how that code in the standard library doesn't error in the same way?"* -- @ElliotChance because there's no expression in the standard library that would violate the rules laid out in the spec, e.g. there's no `fmt.Println(_S, _W, _B, _M)` in the std lib. – mkopriva Jul 11 '21 at 14:35