当前位置: 首页 > 面试题库 >

为什么在golang中没有左移64位溢出?

岳时铭
2023-03-14
问题内容

我正在看《A Tour of Go》,我对他们的basic-
types.go示例感到困惑:

MaxInt uint64     = 1<<64 - 1

难道不应该在无符号的64位整数中向左移动1 64个位置会导致溢出(也就是比MSB高一点)吗?

但是,直到该行更改为:编译器才会抱怨:

MaxInt uint64     = 1<<65 - 1

./basic-types.go:5: constant 36893488147419103231 overflows uint64

如果我编写一些代码以迭代不同长度的左移,包括如上例中那样导致导致编译器发声的左移65,我会看到两件事:

  1. 它的行为符合我的预期,1<<63因为它会将uint64中的1放置在MSB中

  2. 它不再溢出了(呵呵!!!!)

码:

package main

import "fmt"

func main() {
    for i := 60; i < 66; i++ {
        var j uint64 = 1 << uint64(i) - 1
        fmt.Printf("%2d | %64b | %#18x\n", i, j, j)

    }

输出:

60 |     111111111111111111111111111111111111111111111111111111111111 |  0xfffffffffffffff
61 |    1111111111111111111111111111111111111111111111111111111111111 | 0x1fffffffffffffff
62 |   11111111111111111111111111111111111111111111111111111111111111 | 0x3fffffffffffffff
63 |  111111111111111111111111111111111111111111111111111111111111111 | 0x7fffffffffffffff
64 | 1111111111111111111111111111111111111111111111111111111111111111 | 0xffffffffffffffff
65 | 1111111111111111111111111111111111111111111111111111111111111111 | 0xffffffffffffffff

问题答案:

当你写

1<<64

1上面没有一个int64。这是一个 常量常量 。根据语言规格:

常量表达式总是精确地求值;中间值和常量本身可能需要比该语言中任何预声明类型支持的精度大得多的精度。

因此,常量文字会在编译时进行评估,因为它不是语言实现的特定类型,所以它可能非常大。

下面实际上会给出一个溢出错误:

var i int64
i = 1<<65 - 1

因为现在常量文字表达式求值的值大于int64可以包含的值。

在此处阅读有关此内容的更多信息。

要知道您的示例代码为何适用i = 65,请参考Golang
规范中的以下规范:

移位表达式中的右操作数必须具有无符号整数类型,或者是可以转换为无符号整数类型的无类型常量。如果 非恒定移位表达式
左操作数是未类型化的常数,则首先将其转换为假定移位表达式被其左操作数单独替换时所假定的类型

上面的膨胀部分与您的代码有关。考虑下面的代码:

a := 66
var j uint64 = 1<<uint64(a) - 1

在移位运算符中,右操作数是 一个非恒定的表达式 。因此整个移位操作成为 非恒定移位表达式 。因此,如上所述,将左操作数1转换为uint64

现在,已在上uint64(1)进行了平移,可以使用所需的任意<<多个位置进行平移。您可以将其移位到64位以上,并且实现将很容易地允许它。但是在这种情况下,保存uint64(1)上述内容的内存将包含全零。

请注意,根据语言规范,此行为与溢出并不相同。同样,只要正确的运算符不是常量表达式,语言限制就可以进行任意多的移位。因此,例如,这将起作用:

a := 6666
var j uint64 = 1<<uint64(a) - 1 // non-constant shift expression

这样想吧。较早时,未输入1。它具有任意精度(取决于实现),并且正在返回整数(所有位)。现在,由于它是a uint64,因此仅考虑了前64位。

这仍然会导致溢出,因为左操作数1是untypes,并且可能包含大量位,对于a返回的值太大uint64

var j uint64 = 1<<uint64(66) - 1 // overflow. Note that uint64(64)
fmt.Println(j)                   // is typed, but it's still a constant


 类似资料:
  • 今天我学习了左移位运算符()。正如我所理解的,左移位运算符将位移动到指定的左边。我也知道乘以2表示移位。但是我很困惑,比如“移位”到底是什么意思,为什么当赋值的类型不同时,输出会有所不同? 当我调用下面的函数时,它给出的输出为

  • 问题内容: 最近,我一直在对我公司的数据库产品的写入性能进行一些基准测试,并且发现仅切换到64位JVM可以使性能持续提高20-30%。 我不允许详细介绍我们的产品,但基本上它是面向列的数据库,已针对存储日志进行了优化。基准测试包括向其提供几GB的原始日志,并确定分析它们并将其作为结构化数据存储在DB中所需的时间。CPU和I / O的处理非常繁重,尽管很难说是什么比例。 有关设置的一些注意事项: 两

  • 我必须编写一个函数,对y位置的位进行左循环移位。例如,如果我将:01011000和2作为y,则函数必须返回011000001。 我已经尝试使用但它似乎是无用的。

  • 问题内容: 我来自C语言,正在学习Python。缺乏显式的类型安全性令人不安,但是我已经习惯了。面对动态语言的所有优点,缺少内置的基于契约的编程(纯抽象类,接口)的不足是必须习惯的。 但是,无法请求const- cortectness使我疯狂!为什么Python中没有常量?为什么不鼓励使用类级常量? 问题答案: C和Python属于两种不同的语言类别。 前一个是 静态 类型的。后者是 动态的 。

  • 问题内容: 我正在尝试从Firestore数据库中的python程序中存储64位整数。问题在于,似乎最后一位数字已四舍五入。 当我查看数据库时,它们存储为: 根据文档,支持64位带符号整数。可能是什么问题? 问题答案: 我不确定100%,但是我的猜测是您看到的是JavaScript整数的大小不是64位。它们实际上更像是53位。由于Firebase控制台是使用JavaScript实现的网络应用,因此

  • 问题内容: 在Java中,为什么以下代码行不起作用? 如果我将其更改为 起初,我以为您可能没有接口列表,但是我可以创建一个很好的接口。 有想法吗? 问题答案: 泛型类型比较古怪。 表示或任何子类型,但仅表示。如果您想要一个子类型,您需要 我怀疑你可以用 无法执行此操作的原因是,您可以使用对引用的引用,并且必须谨慎使用额外的间接级别。 使用泛型,您可以有两个间接级别,这会给您带来问题,因此它们更容易