简短摘要(TL; DR)在答案的结尾。
无类型的任意精度常量在运行时不存在,常量仅在编译时(在编译期间)存在。话虽如此,Go不必在运行时以任意精度表示常量,而仅在编译应用程序时。
为什么?因为常量不会被编译到可执行二进制文件中。他们不必是。让我们举个例子:
const Huge = 1e1000fmt.Println(Huge / 1e999)
有一个不变
Huge的 源代码
(并会在包对象),但它不会出现在你的可执行文件。相反,
fmt.Println()将记录一个传递给它的值(类型为)来记录对函数的调用
float64。因此在可执行文件中,只会记录一个
float64值
10.0。
1e1000可执行文件中没有任何数字的迹象。
此
float64类型派生自 无类型 常量的 默认
类型。是浮点文字。验证:
__
Huge``1e1000
const Huge = 1e1000x := Huge / 1e999fmt.Printf("%T", x) // Prints float64回到任意精度:
规格:常数:
数字常数表示任意精度的精确值,并且不会溢出。
因此,常数表示任意精度的精确值。如我们所见,无需在 运行时 以任意精度表示常量,但编译器仍必须在 编译时 执行某些 操作 。它 确实 !
显然,“无限”精度无法解决。但是没有必要,因为源代码本身不是“无限的”(源大小是有限的)。但是, 允许
真正任意精度仍然不切实际。因此,规范为此提供了一些自由给编译器:
实现限制:尽管数字常量在语言中具有任意精度,但是编译器可能会使用内部表示形式来实现它们,而精度有限。也就是说,每个实现都必须:
- 用至少256位表示整数常量。
- 表示浮点常数,包括复数常数的部分,尾数至少为256位,带符号的指数至少为32位。
- 如果无法精确表示整数常数,则给出错误。
- 如果由于溢出而无法表示浮点数或复数常量,则给出错误。
*如果由于精度限制而无法表示浮点数或复数常数,则四舍五入到最接近的可表示常数。这些要求既适用于文字常量,也适用于评估常量表达式的结果。
但是,还请注意,当上述所有内容都存在时,标准包为您提供了仍然可以以“任意”精度表示和使用值(常数)的方法,请参见package
go/constant。您可以查看其来源,以了解其实现方式。
实现在中
go/constant/value.go。表示此类值的类型:
// A Value represents the value of a Go constant.type Value interface { // Kind returns the value kind. Kind() Kind // String returns a short, human-readable form of the value. // For numeric values, the result may be an approximation; // for String values the result may be a shortened string. // Use ExactString for a string representing a value exactly. String() string // ExactString returns an exact, printable form of the value. ExactString() string // Prevent external implementations. implementsValue()}type ( unknownVal struct{} boolVal bool stringVal string int64Val int64 // Int values representable as an int64 intVal struct{ val *big.Int } // Int values not representable as an int64 ratVal struct{ val *big.Rat } // Float values representable as a fraction floatVal struct{ val *big.Float } // Float values not representable as a fraction complexVal struct{ re, im Value })如您所见,该
math/big包用于表示无类型的任意精度值。
big.Int例如(来自
math/big/int.go):
// An Int represents a signed multi-precision integer.// The zero value for an Int represents the value 0.type Int struct { neg bool // sign abs nat // absolute value of the integer}nat(来自
math/big/nat.go)在哪里:
// An unsigned integer x of the form//// x = x[n-1]*_B^(n-1) + x[n-2]*_B^(n-2) + ... + x[1]*_B + x[0]//// with 0 <= x[i] < _B and 0 <= i < n is stored in a slice of length n,// with the digits x[i] as the slice elements.//// A number is normalized if the slice contains no leading 0 digits.// During arithmetic operations, denormalized values may occur but are// always normalized before returning the final result. The normalized// representation of 0 is the empty or nil slice (length = 0).//type nat []Word
最后
Word是(来自
math/big/arith.go)
// A Word represents a single digit of a multi-precision unsigned integer.type Word uintptr
摘要
在运行时: 预定义
类型提供有限的精度,但是您可以使用某些包(例如
math/big和)“模拟”任意精度
go/constant。在编译时:常量似乎提供了任意精度,但实际上,编译器可能无法做到这一点(不必这样做);但是规范仍然为所有编译器必须支持的常数提供了最低的精度,例如,整数常数必须至少以256位(32字节)表示(相比之下
int64,“仅”
8字节)。
创建可执行二进制文件时,必须转换常量表达式的结果(具有任意精度),并用有限精度类型的值表示-这可能是不可能的,因此可能导致编译时错误。请注意,只有
结果( 而不是中间操作数)必须转换为有限精度,常数运算可以任意精度执行。
规范未定义如何实现这种任意精度或增强精度,
math/big例如,将数字的“数字”存储在切片中(其中,数字不是以10为基数表示的数字,而“数字”
uintptr是类似于base的数字)。在4位元架构上显示4294967295,在64位元架构上甚至更大)。



