常量是 Go 里最“数学”的一块:无类型常量按任意精度计算,只有在赋给变量或带类型时才受范围和精度限制。这篇用真实输出验证 iota 的各种写法,然后用对照实验展示精确计算的效果,再列出常见的编译错误并测出无类型整数常量的实际上限。
Weekday:Sunday = 0、Monday = 1、Tuesday = 2、Thursday = 4(用 _ 跳过了 3) ByteSize:KB = 1024、MB = 1.048576e+06、GB = 1.073741824e+09;%T = main.ByteSize 一行多个:A, B = 0, 0;C, D = 1, 10;E, F = 2, 20 位标志:FlagRead = 1、FlagWrite = 2、FlagExec = 4、FlagAll = 7 隐式重复与 iota:X = 0、Y = "字符串"、Z = "字符串"(沿用上一行的表达式)、W = 3(iota 仍按行号递增) 另一个 const 块里 iota 重新从 0 开始:Z2 = 0
iota 是 const 块里的行号(从 0 开始,每个块重置)。省略表达式的行会沿用上一行的表达式,所以 Z 得到上一行的字符串、而 W = iota 重新用行号 3。一行里多个名字可以各用一次 iota。1 << iota 是位标志的惯用写法,1 << (10 * iota) 加上带类型的 float64 就是 KB/MB/GB。
const big = 1 << 100;big >> 98 = 4(int 装不下 big,但常量表达式可以) const third = 1.0 / 3;third * 3 == 1 → true;float64 变量:x := 1.0 / 3.0; x*3 == 1 → true 常量 0.1 + 0.2 == 0.3 → true;变量 x + y == 0.3 → false;x + y = 0.30000000000000004 整数常量除法 1/2 = 0;1/2.0 = 0.5;7/2 = 3;-7/2 = -3;7.0/2 = 3.5 math.Pi 的常量精度:float64(math.Pi) = 3.14159265358979311600;math.Pi*1e30/1e30 == math.Pi → true 默认类型:int float64 int32 string complex128 var f32 float32 = 16777217 → 1.6777216e+07(float32 只能精确表示到 2^24 = 16777216)
无类型常量的运算是精确的:0.1 + 0.2 作为常量就是精确的 0.3,所以和 0.3 相等;一旦落到 float64 变量,0.1 与 0.2 各自先取近似,和就变成 0.30000000000000004。同样,1 << 100 可以作为中间量参与常量运算,只要最后的结果能放进目标类型。整数常量相除是整数除法(1/2 = 0),只要有一个操作数是浮点常量就按浮点算。
const timeout = 5 * time.Second → 5s;1500 * time.Millisecond → 1.5s(整数倍的常量乘法)
n := 3;time.Duration(n) * time.Second → 3s
len("abcd") 是常量,可以作数组长度:[len("abcd")]int 占 32 字节time.Second 是 time.Duration(底层 int64)类型的常量,所以 5 * time.Second 没问题,但 1.5 * time.Second 要把 1.5 转成 int64,编译器报“truncated”,正确写法是 1500 * time.Millisecond。变量与常量不同,n * time.Second 里 n 是 int,类型不匹配,必须先转换成 time.Duration。
var x int8 = 200:
main.go:3:14: cannot use 200 (untyped int constant) as int8 value in variable declaration (overflows)
const c int32 = 1 << 31:
main.go:3:17: cannot use 1 << 31 (untyped int constant 2147483648) as int32 value in constant declaration (overflows)
const half = 1.5 * time.Second(time.Second 是带类型的整数常量):
main.go:5:14: 1.5 (untyped float constant) truncated to int64
const huge = 1 << 1000 是合法的常量,但赋给 int 变量:
main.go:3:16: constant shift overflow
const huge = 1 << 1000;huge >> 995 = ?:
main.go:5:16: constant shift overflow
n := 3; n * time.Second(类型不匹配):
main.go:5:27: invalid operation: n * time.Second (mismatched types int and time.Duration)
var s uint = 1; var f float64 = 1 << s(非常量移位):
main.go:3:47: invalid operation: shifted operand 1 (type float64) must be integer
常量整数除以 0:
main.go:3:23: invalid operation: division by zero
x := 10; var f float64 = x(变量不能隐式转换):
main.go:3:40: cannot use x (variable of type int) as float64 value in variable declaration
const 用函数结果初始化:
main.go:5:13: time.Now() (value of struct type time.Time) is not constant
二分法找无类型整数常量的上限:const c = 1 << k 能编译并算出 c >> (k-2) 的最大 k = 511(1 << 511 约 6.704e+153);k = 512 报 constant shift overflow;k = 511 时 c >> 509 的运行输出 = 4常量溢出的编译错误会直接写“overflows”。无类型整数常量并不是真的无限:二分法实测 1 << 511 可以、1 << 512 报 constant shift overflow,与编译器用约 512 位表示常量一致(这个“512 位”是对实验结果的解释,没有读源码)。此外,非常量的移位在需要浮点结果时是错误,time.Now() 这样的函数调用不能初始化 const。