格式化打印64位整数-1作为十六进制在golang和C之间偏离

时间:2018-03-30 11:16:52

标签: go

我最近阅读了effective_go文档,在阅读 Print 部分时感到震惊:

var x uint64 = 1<<64 - 1
fmt.Printf("%d %x; %d %x\n", x, x, int64(x), int64(x))

打印

18446744073709551615 ffffffffffffffff; -1 -1

作为一名C程序员,%x int64(x)的预期输出也应该是ffffffffffffffff,所以上面的输出对我来说是不合理的,有人能说出原因吗?

3 个答案:

答案 0 :(得分:3)

这里的行为与C不同,例如。整数的%x动词意味着使用十六进制(基数16)表示格式化数字的,而不是其存储器表示(这将是2的补码)。对于二进制%b和八进制表示的%o也是如此。

对于-255这样的负数,其base16表示为-ff

Go对类型严格,你必须要明确。如果传递有符号整数,则将其格式化为有符号整数。如果要将其打印为无符号值,则必须将其显式转换为无符号值,如下例所示:

i := -1 // type int
fmt.Printf("%d %x %d %x", i, i, uint(i), uint(i))

输出(在Go Playground上尝试):

-1 -1 4294967295 ffffffff

请注意,将有符号值转换为无符号版本(相同大小)时,它不会更改内存表示的类型,因此转换后的值(无符号结果)将是已签名的2的补码值。

至于为什么这是负数的默认值,请阅读Rob Pike here给出的推理:

  

为什么这不是默认[无符号格式]?因为如果是的话,就没有办法将某些东西打印为负数,正如你所看到的那样,这是一个更短的表示。换句话说,%b%o%d%x all同样对待他们的论点。

您可以在此相关问题中查看此操作的实现方式/位置:Golang: Two's complement and fmt.Printf

答案 1 :(得分:3)

  

C编程语言,第二版

     

x,X int;无符号十六进制数(没有前导0x或0X),   使用abcdef或ABCDEF表示10,...,15。

     

x,X unsigned int;无符号十六进制表示法(没有前导0x   或0X),使用abcdef表示0x或ABCDEF表示0X。

     

GNU C Library Reference Manual,Version 2.27

     

'%x','%X'将整数打印为无符号十六进制数。 '%X'   使用小写字母,'%X'使用大写字母。

我感到震惊 - 发现这里正在进行整数转换!

在C中,隐含的转换比比皆是。在Go中,按设计,转换是明确的。

在C中,printf隐式将有符号整数转换为格式为%x的无符号整数。转而fmt.Printf没有。

按照设计,Go不是C. Go确实与C有相似之处,这可能会让人不知所措。

答案 2 :(得分:0)

int64(math.MaxUint64)溢出int64

的范围
package main

import (
    "fmt"
    "math"
)

func main() {
    var x uint64 = 1<<64 - 1
    fmt.Printf("%v\n", math.MaxUint64 == x) // true

    var y int64 = 1<<63 - 1
    fmt.Printf("%v\n", math.MaxInt64 == y) // true

    var z int64 = int64(x) // -1
    fmt.Printf("%v\n", z)
}

游乐场:https://play.golang.org/p/J6buiaaZcFt

请参阅https://golang.org/pkg/math/#pkg-constantshttps://play.golang.org/p/iul2dgRbK2E