Go: 提案:规范:二进制整数文字

创建于 2017-02-27  ·  91评论  ·  资料来源: golang/go

目前,除了您期望的标准十进制文字外,Go 还支持八进制和十六进制整数文字。 为了完善这个小组,我还建议添加二进制整数文字。 它们将以整数文字的新前缀形式出现: 0b0B

初步免责声明

这是我第一次深入了解 Go 源代码,主要是一次学习经验,让我学会了提交更改等等。 话虽如此,我感谢任何和所有批评、评论和建议。

“为什么”:现有技术

二进制文字也存在或已经出现在主流语言中,包括:

以上所有案例都采用了使用0b0B作为二进制文字前缀的约定,这表明这对于 Go 来说也是一个相当舒适和明智的选择,避免了不必要的发明和为来自上述其他语言的程序员提供相似性。

“为什么”:续

我设法找到了一些与此相关的早期讨论,尽管这是在我已经实现了该功能之后,它更多地与更改八进制语法有关,而不是专门与二进制文字相关。 但是来自@griesemer 的https://github.com/golang/go/issues/12711#issuecomment -142338246 确实提到“在 Go 的设计中也讨论了八进制的 'o' 和二进制符号的 'b'。这根本不够划算。” 但是,我不认为这是反对在语言中添加如此简单的东西的好理由。 特别是考虑到如今越来越多的语言采用二进制文字的语法这一事实,似乎需要以新的眼光看待早期的 Go 设计决策。

示例用法

const (
    SOME_MASK   = 0b00001111
    SOME_FLAG_A = 0b00000001
    SOME_FLAG_B = 0b00000010
    SOME_FLAG_C = 0b00000100
    SOME_FLAG_D = 0b00001000
)

执行

正如我所说,这对我来说更像是一种学习体验,我已经准备好实现此功能的更改:

CL-37502规范:指定二进制整数文字的语法
CL-37503 cmd/compile/internal/syntax:扫描二进制文字
CL-37504 go/scanner:扫描二进制整数文字
CL-37505 strconv:支持解析二进制整数文字
CL-37506测试:使用二进制文字使用扩展 int_lit

FrozenDueToAge Go2 LanguageChange NeedsDecision Proposal Proposal-Accepted

最有用的评论

让我们看看为什么上述语言向前发展并增加了对二进制文字的支持。 让我们从 C++14 开始,因为它是列表中的第一个。 当时的 Google 员工 James Dennett 提出了什么观点?

对二进制文字使用 0b/0B 前缀是现有的 GCC 扩展(也受 Clang 支持),并且与 Java 7、Python 和 D 的语法相同。

为什么这个特殊的点有利于 Go?

熟悉语言。

许多开发人员从编程语言转向编程语言。 如果我错了,请纠正我,但我们都尝试用另一种语言从一种语言中了解我们的知识。 你可以在某种程度上说 Go 对 C 和 C++ 的熟悉吸引了那些想要像 GC 这样的特定功能但不喜欢其他语言的开发人员。 这是我目前实习的公司选择转向 Go 的原因之一。

除了有经验的开发人员,我们也来看看熟悉对于初学者的开发人员有什么好处。 我们还以@eddieringle提到的标志的用例

最后我想在这里补充一点,每种语言的目的(至少这是我希望的)都是编写干净的代码。 我认为这是我们都同意的事情,无论如何。 在查看其他人的代码时,无需任何解释就可以立即清楚地知道,当拥有二进制文字常量列表时,这些是标志。 当使用十六进制或八进制时,同样的事情就不那么简单了。 下面是一个比较。

// Hexadecimal
const (
    MASK          = 0x1E
    DEFAULT_COLOR = 0x00
    BOLD          = 0x01
    UNDERLINE     = 0x02
    FLASHING_TEXT = 0x04
    NO_CHANGE     = 0x08
)

// Octal
const (
    MASK          = 036
    DEFAULT_COLOR = 00
    BOLD          = 01
    UNDERLINE     = 02
    FLASHING_TEXT = 04
    NO_CHANGE     = 010
)

// Binary
const (
    MASK          = 0b11110
    DEFAULT_COLOR = 0b00000
    BOLD          = 0b00001
    UNDERLINE     = 0b00010
    FLASHING_TEXT = 0b00100
    NO_CHANGE     = 0b01000
)

我确实认为不需要考虑最后一个常量用于标志这一事实。 这些也是很少的标志,所以请记住,当有更多标志时,这肯定会加起来。 第一个常量0x1E在没有上下文的情况下声明时肯定会引起一些人的注意。 单独使用二进制文字可能表明变量可能用作标志。

引用的 C++ PDF 进一步引用了上述语言的支持。 那么让我们看看接下来的那些。 我在 2009 年找到了 Derek Foster 提出的(原始?)提议,用于 JDK 中的二进制文字。 来源

我完全同意它的第一个问题是,为什么 JDK 中有八进制表示,而 JDK 中没有二进制表示。 在过去的几年里,我从来没有想过:“哦,八进制会让我的代码更干净!” 然而,这指的是我之前提出的一点:熟悉度。 然而,它增加了我之前提出的观点:

然而,当处理的数据基本上是面向位的时,使用十六进制表示位范围需要程序员进行额外程度的转换,这通常会成为错误的来源。 [...] 那么按照该规范进行编码的程序员必须将每个这样的值从其二进制表示转换为十六进制。 [...] 在大多数情况下,程序员会在脑海中进行这些翻译,并希望能把它们弄对。 然而,错误很容易蔓延,重新验证结果也不够直接,不能经常进行。

主要用于硬件而不是二进制的十六进制和八进制表示法可能会导致人为错误。 在我之前给出的比较中,我通过将我认为是八进制的内容输入谷歌来检查我的头脑所做的事情,这证实了我的答案。 当我用二进制写它时,我自动确定我的情况,但当我用十六进制或八进制写它时却不是。 无论您一天执行多少次,都会使编写代码变得更加困难,因为您必须在头脑中考虑二进制形式,并且在这样做时可能会出错。

为了深入探讨为什么有八进制符号但没有二进制符号的问题,我还有另一个问题要问,JDK 二进制文字提案的作者 Derek Foster 也提出了这个问题:“为什么 Go 选择使用八进制符号的0前缀?” @griesemer评论说我们在实现新功能时不应该跳枪:

让我们拭目以待,看看其他人怎么说,然后再跳枪。 谢谢。

但是在实现八进制表示法时,Go 不是跳了起来吗? 如果它的论点是“因为其他语言这样做”,那么为什么该论点不能用于二进制文字? 如果不是,那么八进制符号的0前缀在混淆人们时将其纳入语言的原因是什么?

有人可能会错误地认为“0b1”表示与十六进制数“0xB1”相同的值。 但是,请注意,这个问题对于八进制/十进制已经存在多年(“050”和“50”之间的混淆)并且似乎不是主要问题。

-德里克·福斯特

似乎没有更多的观点支持二进制文字,因为它是我们所有人都在脑海中提到的东西。 这就是为什么我觉得对其他语言的建议像这样简短。 然而,这不是这么快关闭它的理由。

所有91条评论

以前也出现过这种情况。 推出此功能涉及大量工作,使编译器和规范更改变得微不足道。 但是有很多库也应该保持一致(strconv、math/big 等)。

如果我们在这个方向上做出改变,它应该更彻底,并支持任意基础。 我反对原样。

@griesemer是的,我即将提交的更改也会修改strconv (据我所知,实际上需要支持此更改)。

@griesemer但是,我不同意任何更改都应支持任意基础,否则根本不进行任何更改。 从之前的阅读来看,这听起来像是 Go2 的一个很好的目标; 这只是与其他语言的语法开发人员在使用 Go 时可能期望的一起完善 Go1。 (即 Base-2 是一个足够常见的情况,可能比八进制更常见;base-14 或你拥有的东西不太常见。)

CL https://golang.org/cl/37503提到了这个问题。

CL https://golang.org/cl/37504提到了这个问题。

CL https://golang.org/cl/37502提到了这个问题。

CL https://golang.org/cl/37505提到了这个问题。

CL https://golang.org/cl/37506提到了这个问题。

但是,我不认为这是反对在语言中添加如此简单的东西的好理由。

这也不是支持添加它们的特别有力的论据。

恕我直言,您需要扩展“为什么”部分,准确解释支持二进制文字将为编写 go 代码的人带来哪些优势。

我不觉得它们特别有用。 对于具有“位级含义”的文字,hex 是一种更具可读性和紧凑性的格式。

你给出了一个“使用示例”,但它不是很引人注目。 我会使用0xf s 编写这些常量,并为其他人转换。

@EddieRingle这个提议没有被广泛讨论,也没有被接受。 请不要向我们发送代码审查垃圾邮件。 Go 团队有足够的时间处理真正重要的工作。

每个人都清楚,向语言添加一个简单的特性是微不足道的。 很明显,很多人会喜欢这个功能(我自己有时也会喜欢它)。 但这就是说,仅仅因为一个人可以,并不是一个人应该的论点。 对语言的任何小而简单的添加都会带来长期成本。 如果我们接受了这一点,那么将来要拥有更通用的机制将变得更加困难,我们需要保持向后兼容。

让我们拭目以待,看看其他人怎么说,然后再跳枪。 谢谢。

提醒我们禁止我过于政策: https :

没有建设性内容的意见可以使用 Github 的表情符号反应来表达。

@ALTree

恕我直言,您需要扩展“为什么”部分,准确解释支持二进制文字将为编写 go 代码的人带来哪些优势。

我不觉得它们特别有用。 hex 是一种更具可读性和紧凑性的格式,用于具有“位级含义”的文字,IMO。

事实上,我会反其道而行之。 在许多情况下,十六进制更紧凑,是的,但二进制文字将是精确的“位级”表示,因此尽可能具有可读性。

@griesemer

这个提议没有被广泛讨论,也没有被接受。 请不要向我们发送代码审查垃圾邮件。 Go 团队有足够的时间处理真正重要的工作。

道歉。 它最初是一个单一的更改,但由于 Go 策略似乎是根据受影响的代码库区域拆分提交,这就是我最终将它们拆分的方式。 我不知道机器人会在此处针对每次更改发表个人评论。 然而,我不会冷酷地称之为向您发送垃圾邮件,也不会暗示我利用空闲时间所做的任何努力都不重要。

对语言的任何小而简单的添加都会带来长期成本。 如果我们接受了这一点,那么将来要拥有更通用的机制将变得更加困难,我们需要保持向后兼容。

就像之前提到的那样,通用路由(我也更喜欢)也会鼓励弃用/删除现有的(令人困惑的)八进制语法,不是吗? 我的感觉是,通用语法(例如, 2r00102x0010用于 base-2)是为 Go2 发明的,无论如何,突破性的更改都是受欢迎的。

把潜在的 Go2 放在一边,以解决“_如果我们接受这一点,将来拥有更通用的机制将变得更加困难_”的说法:我只是不明白这是怎么回事。 添加二进制文字前缀将与替代的通用语法正交,尤其是您在 #12711 中描述的语法(实际上,该语法与十六进制文字直接冲突,但不会与此提议的二进制文字语法冲突)。 它们将并排存在,就像通用语法与现有的八进制、十六进制和十进制文字一样。

道歉。 它最初是一个单一的更改,但由于 Go 策略似乎是根据受影响的代码库区域拆分提交,这就是我最终将它们拆分的方式。 我不知道机器人会在此处针对每次更改发表个人评论。 然而,我不会冷酷地称之为向您发送垃圾邮件,也不会暗示我利用空闲时间所做的任何努力都不重要。

不仅仅是机器人发送关于 CL 的邮件,而且每个邮寄的 CL 都是要求 Go 审查者花时间审查它的请求。

0b语法很好,因为它很熟悉,但如果真正的目标只是向语言添加二进制文字,我更喜欢通用解决方案而不是熟悉的解决方案。

是否有任何技术原因无法在 2.0 之前实现通用选项? 我最近遇到过很多情况,二进制文字比十六进制更受欢迎,最好在 1.9 或 1.10 中使用该选项,而不是等待(可能很多年)直到 2.0。

@wedow我认为查看二进制文字有用的具体实际情况会有所帮助。 请分享二进制文字会有所帮助的情况。 谢谢。

我不认为“应该支持任意基础”是值得反对的。 它增加了复杂性/成本,但很少或没有额外的好处。 在所有我一直在黑客的几年里,想成为无用的基地我听到人们希望的使用是2,8,10, 12 ,16,可能64(我们base64编码,毕竟) .

让我们看看为什么上述语言向前发展并增加了对二进制文字的支持。 让我们从 C++14 开始,因为它是列表中的第一个。 当时的 Google 员工 James Dennett 提出了什么观点?

对二进制文字使用 0b/0B 前缀是现有的 GCC 扩展(也受 Clang 支持),并且与 Java 7、Python 和 D 的语法相同。

为什么这个特殊的点有利于 Go?

熟悉语言。

许多开发人员从编程语言转向编程语言。 如果我错了,请纠正我,但我们都尝试用另一种语言从一种语言中了解我们的知识。 你可以在某种程度上说 Go 对 C 和 C++ 的熟悉吸引了那些想要像 GC 这样的特定功能但不喜欢其他语言的开发人员。 这是我目前实习的公司选择转向 Go 的原因之一。

除了有经验的开发人员,我们也来看看熟悉对于初学者的开发人员有什么好处。 我们还以@eddieringle提到的标志的用例

最后我想在这里补充一点,每种语言的目的(至少这是我希望的)都是编写干净的代码。 我认为这是我们都同意的事情,无论如何。 在查看其他人的代码时,无需任何解释就可以立即清楚地知道,当拥有二进制文字常量列表时,这些是标志。 当使用十六进制或八进制时,同样的事情就不那么简单了。 下面是一个比较。

// Hexadecimal
const (
    MASK          = 0x1E
    DEFAULT_COLOR = 0x00
    BOLD          = 0x01
    UNDERLINE     = 0x02
    FLASHING_TEXT = 0x04
    NO_CHANGE     = 0x08
)

// Octal
const (
    MASK          = 036
    DEFAULT_COLOR = 00
    BOLD          = 01
    UNDERLINE     = 02
    FLASHING_TEXT = 04
    NO_CHANGE     = 010
)

// Binary
const (
    MASK          = 0b11110
    DEFAULT_COLOR = 0b00000
    BOLD          = 0b00001
    UNDERLINE     = 0b00010
    FLASHING_TEXT = 0b00100
    NO_CHANGE     = 0b01000
)

我确实认为不需要考虑最后一个常量用于标志这一事实。 这些也是很少的标志,所以请记住,当有更多标志时,这肯定会加起来。 第一个常量0x1E在没有上下文的情况下声明时肯定会引起一些人的注意。 单独使用二进制文字可能表明变量可能用作标志。

引用的 C++ PDF 进一步引用了上述语言的支持。 那么让我们看看接下来的那些。 我在 2009 年找到了 Derek Foster 提出的(原始?)提议,用于 JDK 中的二进制文字。 来源

我完全同意它的第一个问题是,为什么 JDK 中有八进制表示,而 JDK 中没有二进制表示。 在过去的几年里,我从来没有想过:“哦,八进制会让我的代码更干净!” 然而,这指的是我之前提出的一点:熟悉度。 然而,它增加了我之前提出的观点:

然而,当处理的数据基本上是面向位的时,使用十六进制表示位范围需要程序员进行额外程度的转换,这通常会成为错误的来源。 [...] 那么按照该规范进行编码的程序员必须将每个这样的值从其二进制表示转换为十六进制。 [...] 在大多数情况下,程序员会在脑海中进行这些翻译,并希望能把它们弄对。 然而,错误很容易蔓延,重新验证结果也不够直接,不能经常进行。

主要用于硬件而不是二进制的十六进制和八进制表示法可能会导致人为错误。 在我之前给出的比较中,我通过将我认为是八进制的内容输入谷歌来检查我的头脑所做的事情,这证实了我的答案。 当我用二进制写它时,我自动确定我的情况,但当我用十六进制或八进制写它时却不是。 无论您一天执行多少次,都会使编写代码变得更加困难,因为您必须在头脑中考虑二进制形式,并且在这样做时可能会出错。

为了深入探讨为什么有八进制符号但没有二进制符号的问题,我还有另一个问题要问,JDK 二进制文字提案的作者 Derek Foster 也提出了这个问题:“为什么 Go 选择使用八进制符号的0前缀?” @griesemer评论说我们在实现新功能时不应该跳枪:

让我们拭目以待,看看其他人怎么说,然后再跳枪。 谢谢。

但是在实现八进制表示法时,Go 不是跳了起来吗? 如果它的论点是“因为其他语言这样做”,那么为什么该论点不能用于二进制文字? 如果不是,那么八进制符号的0前缀在混淆人们时将其纳入语言的原因是什么?

有人可能会错误地认为“0b1”表示与十六进制数“0xB1”相同的值。 但是,请注意,这个问题对于八进制/十进制已经存在多年(“050”和“50”之间的混淆)并且似乎不是主要问题。

-德里克·福斯特

似乎没有更多的观点支持二进制文字,因为它是我们所有人都在脑海中提到的东西。 这就是为什么我觉得对其他语言的建议像这样简短。 然而,这不是这么快关闭它的理由。

这是另一个选项,对我来说似乎比任何整数常量都更清楚。

// Shifts
const (
    MASK          = 0x1e
    DEFAULT_COLOR = 0
    BOLD          = 1<<0
    UNDERLINE     = 1<<1
    FLASHING_TEXT = 1<<2
    NO_CHANGE     = 1<<3
)

(并且掩码不应该是 0xf,而不是 0x1e?)

我有点反对添加二进制常量,至少在 Go 1 中是这样。不过,我会在 Go 2 中添加它们。 不同的原因是,如果由于某种原因有人被困在 Go 1.8,当 Go 1.9 出现二进制常量时,如果该人代码的(可传递的)导入之一使用二进制常量,那么他们就不能再构建自己的项目使用 Go 1.8。 他们将不得不供应商或升级。 添加向前不兼容的功能会产生一定的成本,这会影响其效用。

我同意我认为不需要 {2,8,10,16} 以外的基数。 八进制的情况似乎特别不稳定,我会在 Go 2 中删除八进制。

@randall77我不同意换挡看起来更干净。 在我的脑海中,我仍然将它们表示为二进制数,并且可能总是如此。 删除我在脑海中所做的计算会更容易。

(并且掩码不应该是 0xf,而不是 0x1e?)

MASK名称仅取自JDK 提案,与其他常量并不真正一致。 但它确实表明0x1E和十六进制已经引起混淆。

我可以理解您想将其移至 Go 2 的观点。但我不同意我们应该支持将 Go 版本从 1.9 降级到 1.8 的项目。 这将使语言变化成为处理的噩梦。 然而,我不知道 Go 是如何看待这一点的,遵循 Go 考虑的兼容性是最明智的。

我全心全意支持你在 Go 2 中删除八进制的立场。

我只是重读了我之前的评论(特别是,“围棋团队有足够的工作去做真正重要的工作。”)。 我想为这个声明道歉,这是对我实际想说的相当冒犯的表述。 所以让我再试一次,详细说明一下,希望这次能找到正确的语气:

我们很欣赏那些经过充分证实的建议,并且在必要时附带原型实现。 也就是说,Go 提案过程故意是轻量级的,除非被要求或需要理解提案,否则提案人不需要额外的工作。 发送未请求和/或不解决问题的更改列表会适得其反,因为有人将不得不花时间查看它们(如果只是推迟或关闭它们)。 如果确实想提前原型/编写代码,更好的方法是链接到其他地方的更改(例如,私有 GitHub 提交)。 这将留给 Go 团队和外部贡献者选择:如果他们愿意,他们可以决定查看该代码,或者专注于更高优先级的项目。 谢谢。

@griesemer Gotcha,我理解,这是有道理的。 我认为 Go 团队对待他们的 Gerrit 就像 AOSP 对待他们的一样,并认为在讨论这一点时我的更改可能存在。 无论如何,链接到 GitHub 上的一个分支的工作量较少,所以我想这是双赢的。 :)

我实际上首先做了这项工作,因为我的主要目标是破解编译器。 事后我决定将其作为提案提交。

@AndreasBackx在问题 #151 中讨论了 Go 中八进制的前导 0。 另见#12711。

在定义 1-set-bit 常量时,shift 比0b00001000..00更具可读性,原因很简单,在 shift 版本中,您不需要在屏幕上计算一堆零来了解设置了哪个位; 您只需读取移位值。

0b100000000000000000000000 vs 1 << 23

就现实世界的使用而言,一种常见的变长整数编​​码方法是使用高位来“阅读更多”。 我不得不用它来提取 git packfiles。 这是提取各种基数的低位的代码:

b & 127
b & 0x1f
b & 0177
b & 0b01111111

我个人认为二进制版本更清楚地表明了意图。

您仍然可以使用前面提到的 shift 选项
如果您认为它不可读,请使用辅助函数

b & ^(^0 << 7)
b & mask(7)

@AndreasBackx1<<120b0001000000000000更清楚,因为我不必计算所有这些零。 很明显它是一个掩码,因为12介于1113 ,或者使用iota 。 当必须匹配任意模式时,例如屏蔽指令字中的位,那么十六进制更好,因为习惯于处理位的程序员可以读取 0xae 并“看到”10101110,因为知道 0xa,十,是 1010,助记符十十,就像学习乘法表一样,65 是 ASCII A等。十六进制是一种更密集的表示,对于人类读者来说更容易解析。

@randall77 0644、02775 等等,没有八进制是不是有点乏味? 这就是为什么它仍然在踢。

@RalphCorderoy :是的,在我看来,八进制幸存下来的唯一原因是构建os.FileMode
0664 = 6<<6 + 6<<3 + 4 ,这并不太乏味。 如果os提供符号常量以使这更容易,或者至少更清晰,那就更好了。

我们已经知道如何避免计数零问题:我们应该支持0b1e10以二进制表示 1 后跟 10 个零。 诚然,如果我们有办法连接二进制常量而不是添加它们,这会更好。

我实际上首先做了这项工作,因为我的主要目标是破解编译器。

优秀。 如果您想在讨论此问题时继续破解编译器的地方有一些想法,请随时给我发电子邮件--(github 用户名)@ gmail。

@RalphCorderoy 1<<12 比 0b0001000000000000 更清楚,因为我不必计算所有这些零。

这个问题的解决方案是允许某种分离。 Java允许在数字文字中使用下划线。 它已在#42 中简短地讨论过,但如果首先对该问题有任何评论,则没有太多反对它的论据。

或许也应该考虑@ianlancetaylor的解决方案。

很明显它是一个掩码,因为 12 介于 11 和 13 之间,或者使用 iota。

抱歉,您可能就是这种情况。 但不适用于其他所有人。

当必须匹配任意模式时,例如屏蔽指令字中的位,那么十六进制更好,因为习惯于处理位的程序员可以读取 0xae 并“看到”10101110,因为知道 0xa,十,是 1010,助记符十十,就像学习乘法表一样,65是ASCII A,等等。

正如我之前所说的那样,这为代码中的错误留下了空间,并且为其他语言提出的建议认为这是一个有效的理由。 您还在这里假设每个“程序员”都知道十六进制,但事实并非如此。 您可能会使用很多硬件,但大多数人不会。 绝对是初学者更喜欢二进制文字而不是十六进制表示。

Hex 是一种更密集的表示,对于人类读者来说更容易解析。

密集是否意味着它更干净? 不,它没有。 人们总是为疯狂的事情写单行代码,而那些令人印象深刻的原因是因为代码如此密集且难以阅读,以至于我们都想知道每个字符的含义背后隐藏着什么巫术。

1 << 100b1e10清晰得多。

我发现二进制文字难以阅读。 通常,您需要四舍五入到三或四位段,这在八进制或十六进制读写中更容易且不易出错。 当事情没有达到如此均匀的边界时,转变也更容易读写,而且不容易出错。

某种形式的连接会使二进制文字更易于读写,但代价是不一致。 为什么可以连接二进制文字,而不能连接其他类型的数字文字? 为什么不是十六进制? 至此,这个讨论就变得无止境了。

就个人而言,我更喜欢某种通用基数机制。 我不认为二进制文字带来了足够的影响(而且我只编写低级代码)。

此外,我很确定我们之前曾多次讨论过这个问题。

(作为旁注,八进制的消亡被大大夸大了。八进制除了设置文件模式之外还很有用。我当然更多地使用八进制文字,而不是使用二进制文字。)

看到如此多的个人意见被作为关于改变语言的论据,我感到有点惊讶。 我不确定如何量化与您个人认为它有多有用相关的评论。

如果出于某种原因个人感觉有优点,我会武断地说我在 Java 中使用二进制文字,我可以通过说我已经编程 100 年并且我拥有一辆汽车来验证我的观点。

接下来,争论是否更容易使用 shift 来定义掩码就像争论公历比中国日历更容易使用。 仅仅因为您发现它更易于使用并不意味着每个人都这样做。 二进制文字存在于其他语言中的事实可能表明有人发现它们很有用,进一步说明移位参数不是一个参数,因为它只是一个替代方案。

以前也出现过这种情况。 推出此功能涉及大量工作,使编译器和规范更改变得微不足道。 但是有很多库也应该保持一致(strconv、math/big 等)。

这是反对该提案的有力论据,我完全理解在进行会产生大量工作的更改时犹豫不决。

事实上,我会反其道而行之。 在许多情况下,十六进制更紧凑,是的,但二进制文字将是精确的“位级”表示,因此尽可能具有可读性。

学习二进制的有趣之处在于,您必须实际读写二进制,然后对其进行数学运算。 用十六进制、十进制、八进制或 base64 (lul) 书写可以间接帮助学习二进制,但我听说只学习你想直接学习的东西是有用的(虽然可能只是一个意见)。

就个人而言,我更喜欢某种通用基数机制。

我希望每种语言都有这种文字形式。

@randall77 :正如#151 所说,保持八进制有几个原因。 是的,一个是文件模式的设置,但这是最后一个重要的。 另外两个是语义的变化,它将是一个以 0 开头的整数文字,以及安全移植所有其他类 C 语言的代码的重要性,其中八进制常量具有这种语法。 确实,这些都不是令人信服的,但它们加在一起就达到了标准。 无论如何,问题已经确定,至少对于 Go 1。

至于二进制常数,我认为它们没有重量。 很少有程序会从它们中受益,即使如此,好处也很小。

@robpike只是无法编译任何看起来像八进制常量的东西(以0开头但不是“0”)是安全的。

让我们把这个留给 Go 2。

干嘛要等? 它不会破坏任何东西。
在星期一,2017年3月6日在15:19拉斯考克斯[email protected]写道:

让我们把这个留给 Go 2。


您收到此消息是因为您发表了评论。
直接回复本邮件,在GitHub上查看
https://github.com/golang/go/issues/19308#issuecomment-284535766或静音
线程
https://github.com/notifications/unsubscribe-auth/ABLfW7bN2NicSthvEvMeGEhqExg2et-qks5rjHhtgaJpZM4MNgUY
.

@DocMerlin ,因为 Go 不是一种仅仅因为它可以而不断增加功能的语言。 所有语言更改现在基本上都处于暂停状态,直到它们可以作为一个整体一起进行评估,因此它们的外观和感觉就像一个有凝聚力的整体一样一起工作。 这就是为什么它被标记为 Go2。

@DocMerlin我想在@bradfitz的评论中补充

这些也是很少的标志,所以请记住,当有更多标志时,这肯定会加起来。
SOME_FLAG_D = 0b0000000001000000000000000000000

快速浏览一下,这是 2^19 还是 2^20? 看到问题了吗?

谢谢@davecheney ,我已经赶上了线程。 我没有考虑到让标准库支持二进制整数文字可能需要付出巨大的努力。

很明显,不存在由于缺少二进制文字而无法正确表达处理整数位操作的例程的用例,并且通过以一种基数表示整数数据而不是另一种基数来提高性能。

但是,在许多二进制编码情况(视频编解码器、数据压缩、二进制网络协议等)中,如果数据以基数 2 表示,位掩码、二进制常量和其他位图数据可以在源代码中变得更清晰。

对于处理位图数据的人来说,这是一个易读性和风格的问题。

易读性和风格是 Go 在第一天就支持八进制整数文字符号的原因。 包含对八进制整数文字的支持很可能是与处理 Unix 文件权限有关的决定。 很难想象在这个时代,除了对 Unix 风格权限的传统支持和代码中这些数据的可读性之外,八进制表示法的许多实际用途。

尽管如此,八进制支持有助于表明 strconv 中只有两个简单的函数负责处理八进制字符串。

存档/tar/strconv.go:func (p *parser) parseOctal(b []byte) int64
archive/tar/strconv.go:func (f *formatter) formatOctal(b []byte, x int64)

为了非常粗略地评估添加二进制文字支持的变化影响,一种可能的方法是检查八进制等效支持的代码占用空间,这是一件微不足道的事情,因为八进制很少使用,因此很容易识别以 8 为基数的位置和情况支持。

现在在我的本地副本中,粗略搜索显示其中大部分是解析和格式操作。

vxv@vxs :/gosource$ grep -i -r 八进制 * | wc -l
73
vxv@vxs :/gosource$ grep -i -r 八进制 * | grep "函数" | wc -l
2

诚然,这是一个琐碎而简单的搜索,但问题的比例似乎并不是一项不可逾越的任务。

不用担心。 根据记录,我在这场比赛中没有狗,我只是来解决问题。 由其他人来决定 go 2 提案的命运

但是,在许多二进制编码情况(视频编解码器、数据压缩、二进制网络协议等)中,如果数据以基数 2 表示,位掩码、二进制常量和其他位图数据可以在源代码中变得更清晰。

我已经使用了上述所有内容,而且我从未想过基数 2 会对这些有任何好处。 你能举一个具体的例子来说服我吗?

可读性应该是实现二进制文字的主要原因。 我目前正在编写一个利用位定位优势的引擎,我将 uint16、uint32 用于几个用例,这些 uint 的每个切片/部分代表不同的信息。

在国际象棋中,我们通过在 uint16 中添加标志、from 和 to 位置来使用编码移动。 很高兴看到二进制文字实现,这样代码就可以显示,只有自己,哪些部分与哪些信息相关。

...
constexpr uint_fast16_t FLAG_SPECIAL1  {0b0010000000000000};
constexpr uint_fast16_t FLAG_SPECIAL0  {0b0001000000000000};
constexpr uint_fast16_t RANGE_FLAG     {0b1111000000000000};
constexpr uint_fast16_t RANGE_FROM     {0b0000111111000000};
constexpr uint_fast16_t RANGE_TO       {0b0000000000111111};

这是我来自 C++17 的代码示例。 然而,在 Go 中,它看起来像这样:

const FlagSpecial1 uint16 = 8192
const FlagSpecial2 uint16 = 4096
const RangeFlag uint16 = 61440
const RangeFrom uint16 = 4032
const RangeTo uint16 = 63

对于为使用位和掩码的任何人编写干净简单的代码非常有用。

不能为此获得一个预编译器,然后你就不必重写任何东西了吗? 因为毕竟这是美学(在我看来)。

在 (c++) 中设置了哪个位

constexpr uint_fast16_t FLAG_SPECIAL0  {0b0001000000000000};

对比

在 (Go) 中设置了哪个位

const FlagSpecial0 = 0x10000

我可能不是唯一一个可以在后一种情况下立即告诉的人。

使用 0b00.. 方法您可以看到它,而无需知道十六进制数字。 当您拥有大量 uint16 列表时,阅读起来更容易。 理解设置位在 13 位置,因为列出了大小,在你给出的例子中比使用十六进制更容易。 1<<13 ,会比十六进制好得多,而且您不必查找值,您只需查看它就可以知道目标是哪些位。 但是对于范围或多个设置位,仅使用二进制文字会更容易。

看看后面的情况,如61440您可以立即告诉我印象深刻,并且您认为使用十进制比二进制文字更容易知道哪些位是设置的,但并不是每个人都看到这一点。

但是您只是忽略了其他情况,例如0b0000111111000000是 0xfe0 或十进制 4064。 在我看来,使用二进制文字更干净。 16 位是一个更大的数字,但看看字节:

0b111010100xea对比。 您甚至不必考虑目标是什么,您一看就知道了。

@andersfylling ,如果您正在编写位掩码,那么您确实需要能够读取十六进制:RangeFlag 不会是 61440,而是 0xf000,这很明显它是 16 位的最高半字节。

对于那些认为他们对这个问题有新见解的人,在将我们其他人从沉睡中唤醒之前,请您仔细阅读来自高层的所有评论,特别注意 Brad 对 NoMeToo 政策的引用。

十六进制和位移位为我完成了这一切(加上八进制文件权限),但我想知道使用二进制整数文字在 C 中学习位操作是否更容易。 我喜欢上面的例子在二进制中的样子。 也许我会将它用于小面具或移动小面具。

x := y & (0b101 << 8)

(编辑:更好的 Go 方式是x := y & 0b101<<8

我今天刚遇到这个功能缺失的问题。 在我的示例用例中,我使用日期的整数周字段(0..6 表示星期日..星期六)并根据首选项位掩码检查它。 因为整数的来源是程序化的,所以我没有为一周中的每一天定义一组常量(我的代码没有理由专门讨论 SUNDAY),所以 1 << 3 语法在这里没有用。 但是,我想要首选项位掩码的默认值,可能是 0b0111110。 显然,将此默认值写为十进制 (126) 或十六进制 (0x7e) 很容易,但将其写为二进制要清晰得多。

回复:八进制,请注意在 python2 和 pyhon3 之间,他们放弃了对 0110 格式的支持,现在需要 0o110 代替。 然后,他们将 0o110 解析向后移植到 python2,而不会在那里删除旧格式,从而可以轻松开始使用新的不易出错的语法,而不会破坏与旧版本的兼容性。 在 python2 中,大量 python 用户在粘贴 0 填充的十进制数时不小心声明了八进制数,导致混淆。 (常见问题:来自零件数据库的长度填充序列号,或长度填充发票编号。)实际上,我曾经为此花了半个小时的时间,试图弄清楚为什么我的“显然正确”的单元测试失败了。

另一方面,我从未需要以任意基数支持语言常量。 也许有人有,但这似乎是一个红鲱鱼(支持它所需的语法听起来很丑陋)。

另一个让我着急的例子是在嵌入式协议(I2C、SPI、CAN 等)中定义地址,其中通常将地址定义为数据表中的二进制常量,它具有某种读/写位作为价值的一部分。 将它们转换为十六进制会增加人脑必须做的另一层翻译,因此在调试时还有一件事要问。

@tapir ,请阅读我之前的评论https://github.com/golang/go/issues/19308#issuecomment -352290337 特别是https://github.com/golang/go/wiki/NoPlusOne因为 NoMeToo 现在被称为。

这是一个反对建议,它允许以大约相同(或更少)的成本使用任意基数符号:#28256。 我提出了一个我在各种讨论中直接或间接提到的符号,但从未正式写下来。 请在那里发表意见。

请参阅 #28493(独立提案)讨论使用 _ 作为数字之间的分隔符。

如果您正在重新审视 Go 2 的这个旧的/搁置的讨论,那么我建议同时查看八进制。 显然,您不能删除0123符号(出于兼容性原因 - 至少不是没有弃用期),但是您可以在添加0o123的同时添加0bXXX 。 这将启用一组更一致的基于数字的标识符,促进 Go 语法和程序员期望的良好统一性。

但是,就其本身而言,二进制提案仍然是值得的。

base为 0 时, strconv.ParseInt支持0b010语法?

@nathany是的,如果我们决定在语言中支持 0b 前缀,我们也应该在库中支持它。 例如,编译器目前依赖于 math/big.Int 的 SetString 方法将常量文字转换为 big.Ints - 所以人们会期望 SetString(和朋友,例如 strconv.ParseInt)也能理解 0b。

也许不太明显的是 ParseInt 是否还应该过滤_分隔符,如果我们将其与 #28493 一起接受。 作为单独的路径(将_替换) but for error handling (e.g.; do we allow _` 任何地方,或不在任何地方)和性能我们可能需要在 ParseInt(以及所有其他 ParseXXX 例程的数字)中处理它)。

0o八进制文字是否应该有一个单独的提案/问题,还是最好将它与此放在一起?

0o将匹配 Swift 和 Rust。 我们可以研究他们偏爱这种语法的原因。

我看到比0更喜欢0o一个原因是避免与strconv.ParseInt("012", 0, 64)歧义,其中"012"可能是用户输入。 但是,我不知道这在 Go 中是否与其他语言一样存在很大问题,因为Atoi始终使用基数 10,并且 Go 中没有默认参数,因此程序员必须明确要求派生通过为基数指定 0 来从字符串前缀中提取基数。

不能说我曾经需要在被解析的字符串中使用_ 。 确实不那么明显。

@nathany我建议一个单独的问题。 我同意,如果我们决定允许0b也允许0o保持一致性可能是有意义的(然后我们可能希望 gofmt 自动将0 -prefix 八进制重写为0o前缀八进制,以便前者可以从代码库中慢慢淘汰)。 但是这个提议是关于二进制整数文字的; 让我们继续吧。

我原来的问题没有收到回复,所以我再次阅读了整个帖子,看看为什么会有一个帖子
有这么多竖起大拇指的讨论很少。

到目前为止,提案要求使用二进制文字,原因如下:

其他语言有

该提案详细介绍了其他语言如何使用它们以及其他语言如何使用它们。 这是否曾经是一个足够好的理由?

它们“更具可读性”

我不同意。 它们是一种由 1 和 0 组成的卑鄙模式,没有简单的发音
转换为 base16。

  • 你只知道

0b11101010 与 0xea。 您甚至不必考虑目标是什么,您只需
一看就知道了。

_第一低位、第三低位、第五低位关闭,其余不关闭。 并且有[计算总位数两次以确保数字正确] 8
位总数。_

我知道模式是什么,我可能会记住它几秒钟。 这个知识怎么样
本质上有用吗?

  • Go vs C++

有些论点歪曲了他们的利益,也许是无意的。 在一个特定的例子中
在这个线程中,发布了以下代码段,比较了 go 和 c++。

constexpr uint_fast16_t FLAG_SPECIAL1  {0b0010000000000000};
constexpr uint_fast16_t FLAG_SPECIAL0  {0b0001000000000000};
constexpr uint_fast16_t RANGE_FLAG     {0b1111000000000000};
constexpr uint_fast16_t RANGE_FROM     {0b0000111111000000};
constexpr uint_fast16_t RANGE_TO       {0b0000000000111111};

This is my code example from C++17. In Go however, it will look like this:

const FlagSpecial1 uint16 = 8192
const FlagSpecial2 uint16 = 4096
const RangeFlag uint16 = 61440
const RangeFrom uint16 = 4032
const RangeTo uint16 = 63

问题是 C++ 精心对齐,而 Go 未格式化,缺少const块并且错误地使用十进制而不是十六进制(通过将每个十六进制数字拆分为四个二进制数字,您无法轻松转换为二进制) .

const (
    FlagSpecial1 uint16 = 0x2000
    FlagSpecial2 uint16 = 0x1000
    RangeFlag    uint16 = 0xf000
    RangeFrom    uint16 = 0x0fc0
    RangeTo      uint16 = 0x003f
)

协议规范有时会发布二进制文件

另一个让我着迷的例子是在嵌入式协议中定义地址
(I2C、SPI、> CAN 等...)其中通常有一个地址定义为二进制常量
数据表移位 > 具有某种读/写位作为值的一部分。 转换
它们到十六进制增加了一层人类大脑必须做的翻译,因此又增加了一层
调试时要问的问题。

问题是人脑一开始就不应该为你做这件事。 |

再次考虑您的调试经验。 要将二进制整数文字转储到 stderr 或
然后为他们grep? 您会通过说每个1与同事分享这些数字吗?
0大声说出来? 赔率是你宁愿输出并以十六进制传输它们,如果这是真的,那就是
同样正确的是,源代码应该以十六进制表示这些数字以消除需要
让人类的大脑(或程序)为读者做更多的工作。

许多规范将1010表示为由这些有序状态组成的比特流。 这不会映射到二进制整数文字的逐字节概念,并且肯定会烧伤一些期待
实现一个比特流阅读器。 (我宁愿在标准库中实现一个比特流阅读器而不是支持二进制整数文字)。

我今天刚遇到这个功能缺失的问题。 在我的示例用例中,我使用整数星期几
日期字段(0..6 表示星期日..星期六)并根据首选项位掩码检查它。
因为整数的来源是程序化的,所以我没有为每一天定义一组常量
本周(我的代码没有理由专门谈论 SUNDAY),所以 1 << 3 语法
在这里没用。 但是,我想要首选项位掩码的默认值,即
也许是 0b0111110。 显然,将此默认值写为十进制 (126) 或十六进制 (0x7e) 很容易,但是
用二进制写它要清楚得多。

我会将一周中的几天作为未导出的常量,并通过OR来构建掩码它们的值。 我不同意二进制整数文字在这种情况下有助于使任何事情变得更清楚。

@as感谢您的评论。 它已被记录。

显然我们不需要_二进制整数文字; 我们有一种相当接近的方法来使用十六进制文字来表达这样的数字(我在我的博客文章中已经指出了很多)。 但与此同时,它们似乎解决了许多程序员的痛点,这是我们可以轻松解决的问题,而不会增加语言的复杂性。

也许考虑这个问题的更好方法是我们是否希望在这方面使 Go 与大多数其他编程语言相提并论,并通过支持所有相关的基数 (2, 8, 10, 16) 来完成整型文字表示的集合。

这是一个与关于二进制文字有用性的个人观点不同的问题,可能更容易回答。

更改https://golang.org/cl/152338提到这个问题: spec: add binary integer literals (tentative)

有关与此提案相关的规范更改,请参阅https://golang.org/cl/152338 (在第一步中忽略 _ 分隔符)。

更改https://golang.org/cl/152377提到这个问题: spec: permit underscores for grouping in numeric literals (tentative)

我正在寻找各种排序算法的基准,其中伪随机被设置为0xff & (i ^ 0xab)如果它是0b10101011而不是0xab它将更具可读性。 我很惊讶 Go 中没有二进制文字,甚至没有提案......

@andrewmed这_是_二进制整数文字的提议。

知道了谢谢

我们已经发布了一个联合提案#19308,#12711,#28493和#29008在golang.org/design/19308-number-literals

请注意,这将是遵循博客文章中概述的流程的第一个提案:我们将在 Go 1.13 周期开始时(2 月 1 日)准备好一切并签入,我们将在接下来的三个月中使用这些功能并根据实际使用情况征求反馈,然后在发布冻结开始时(5 月 1 日),我们将做出是否将工作包含在 Go 1.13 中的“启动决定”。

感谢您的反馈以及您对改进 Go 的所有帮助。

最初由@rsc向我建议:弃用(但仍支持)十六进制的 0X,然后不添加 0B(不必要)和 0O(不必要、令人困惑且难以阅读)可能是明智的。

@robpike ...并让 gofmt 开始将 0X 更改为 0x。

@josharian ,是的,我也提出了八进制数 (0644 -> 0o644) 但至少在八进制情况下,我们只能在 goimports 中真正做到这一点,我们知道 go.mod 声明的最低语言版本该模块中的代码。

但是对于 0X -> 0x 可以在 gofmt 中完成,是的。

我对 0B 对 0b 和 0O 对 0o 没有强烈的感觉(许多代码编辑器用斜线写一个零,看起来与大写的 O 不同;我个人总是使用小写)。

但是添加这些新格式的主要目的是与其他语言兼容并减轻来自这些语言的人们的痛苦,也许翻译来自其他地方的代码。 如果上述人员或代码在这些前缀中使用大写字母,而 Go 终究无法消化这些文字,那将破坏该目的。

我还注意到,当我们允许 E 和 e(以及新的 P 和 p)时,与指数会有一些不一致。

简而言之,虽然我完全支持这种情绪,但不允许使用大写 0B 似乎是一种无缘无故的差异,无论如何都无助于习惯使用小写 0b 的人(我猜这是大多数人)并伤害了其他人。

另一方面,让 gofmt 自动(或者可能使用 -s)进行更改似乎是个好主意。

作为一个经常使用单个位和位掩码的嵌入式开发人员,二进制文字将是一个受欢迎的变化。 我最近在 (GC)C 中发现了它们,并感到惊喜。
当然,几乎任何人都可以合理地快速理解0x10x800x8000 。 但是像0x1c这样的东西会让你停下来。 当然, (7 << 2)好一点 0b00011100 只是更具可读性并传达了含义 - 向左 2 个位置的三个连续位 - 更清楚。

更改https://golang.org/cl/157677提到这个问题: cmd/compile: accept new Go2 number literals

更改https://golang.org/cl/159997提到了这个问题: go/scanner: accept new Go2 number literals

更改https://golang.org/cl/160018提到这个问题: cmd/gofmt: test that Go 2 number literals can be formatted

更改https://golang.org/cl/160239提到这个问题: go/constant: accept new Go2 number literals

更改https://golang.org/cl/160240提到这个问题: go/types: add tests for new Go 2 number literals

更改https://golang.org/cl/160247提到这个问题: fmt: scan new number syntax

更改https://golang.org/cl/160250提到这个问题: math/big: add %#b and %O integer formats

更改https://golang.org/cl/160248提到这个问题: text/template: accept new number syntax

更改https://golang.org/cl/160246提到这个问题: fmt: format 0b, 0o prefixes in %#b and %O

更改https://golang.org/cl/160244提到这个问题: strconv: add 0b, 0o integer prefixes in ParseInt, ParseUint

更改https://golang.org/cl/160184提到这个问题: cmd/gofmt: normalize number prefixes and exponents

更改https://golang.org/cl/160478提到这个问题: design/19308-number-literals: add note about gofmt

提醒一下,我们在博客文章 blog.golang.org/go2-here-we-come 中为这些与 Go 2 相关的语言更改引入了一个新流程。 我们将暂时接受提案,在周期开始时更改土地,获得使用经验,然后在三个月后,在冻结时做出最终接受决定。 对于 Go 1.13,这意味着在 2 月份树打开时进行更改,并在 5 月份树冻结时做出最终决定。

我们将暂时接受 Go 1.13 的这个提议,并计划在树打开时实现它。 “暂定接受”的问题状态将被标记为 Proposal-Accepted 但对 Go 版本(此处为 Go1.13)保持开放和里程碑。 在冻结时,我们将重新审视该问题,如果最终被接受,则将其关闭。

更改https://golang.org/cl/161098提到这个问题: spec: document new Go2 number literals

更改https://golang.org/cl/161199提到这个问题: text/scanner: accept new Go2 number literals

更改https://golang.org/cl/163079提到这个问题: text/scanner: don't liberally consume (invalid) floats or underbars

更改https://golang.org/cl/173663提到这个问题: unicode/utf8: use binary literals

更改https://golang.org/cl/174897提到了这个问题: cmd/compile: disable Go1.13 language features for -lang=go1.12 and below

提醒一下,我们在博客文章 blog.golang.org/go2-here-we-come 中为这些与 Go 2 相关的语言更改引入了一个新流程。 Go 1.13 开发周期现已结束,是时候做出最终决定了。

对 Go 2 数字字面变化的反馈非常积极,很少有负面声音。 这些更改使 Go 的数字文字语法现代化和协调,而不会显着增加语言的复杂性:现在三种常见的非十进制数基有统一的前缀表示法,这与其他现代编程语言中使用的表示法相匹配。 十六进制浮点文字的引入解决了人们关心数字代码的痛点。 后缀“i”现在可以与任何(非虚数)数字文字一起使用,以统一的方式创建一个虚数常量。 最后,下划线可用于将较长的文字分成几组数字以提高可读性。

Go 1.13 的提案被接受。 关闭,因为更改已经登陆。

- 用于提案审查的 rsc

更改https://golang.org/cl/189718提到了这个问题: compiler: support new numeric literal syntax

此页面是否有帮助?
0 / 5 - 0 等级