0

I'm recently reading the effective_go document, and got shocked that a %x format printing on an integer value -1 results to "-1":

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

prints

18446744073709551615 ffffffffffffffff; -1 -1

Comes from C language I assume the output of %x int64(x) should also be ffffffffffffffff, I think this is the original purpose of %x yes? Have you ever seen a hexadecimal number getting a sign before it? like this?

-1
-3A
-A3
cifer
  • 615
  • 1
  • 9
  • 25
  • 2
    What is your question? –  Mar 30 '18 at 11:21
  • 1
    math.MaxInt64 = 1<<63 - 1 – Peter Mar 30 '18 at 11:21
  • @LutzHorn sorry, I have clarified my question – cifer Mar 30 '18 at 11:28
  • `fmt.Printf("%x", -10)` prints "-a". Is that what throws you off? – Peter Mar 30 '18 at 11:33
  • 1
    Why do you consider it being wrong to use a sign in front of a negative hex number when you apparently don't consider it being wrong in front of a negative decimal number? – md2perpe Mar 30 '18 at 11:46
  • @Peter @md2perpe because in C/C++, `%x` will print the data in memory true hexadecimally (which means ignore the sign bit, and take 4 bit every time and converts it to hex code util exhausted whole bits). I think golang was consistent with C/C++ at this topic – cifer Mar 30 '18 at 11:56
  • 2
    Yes, I can tell you why it looks unreasonable to you: You think Go should do everything like C. – Volker Mar 30 '18 at 12:27

3 Answers3

5

Go acts differently here than C for example. The %x verb for integers means to format the value of the number using the hexadecimal (base 16) representation, not its memory representation (which would be 2's complement). Same goes for %b for binary and %o for octal representation.

For a negative number like -255, its base16 representation is -ff.

Go is strict about types, you always have to be explicit. If you pass a signed integer, it will be formatted as a signed integer. If you want to print it as an unsigned value, you have to explicitly convert it to an unsigned value like in this example:

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

Output (try it on the Go Playground):

-1 -1 4294967295 ffffffff

Note that when converting a signed value to its unsigned version (same size) it does not change the memory representation just its type, so the converted value (the unsigned result) will be the 2's complement of the signed value.

As to why this is the default for negative numbers, read the reasoning given by Rob Pike here:

Why is that not the default [the unsigned format]? Because if it were, there would be no way to print something as a negative number, which as you can see is a much shorter representation. Or to put it another way, %b %o %d %x all treat their arguments identically.

You can see how / where this is implemented in this related question: Golang: Two's complement and fmt.Printf

icza
  • 389,944
  • 63
  • 907
  • 827
4

The C Programming Language, Second Edition

x,X int ; unsigned hexadecimal number (without a leading 0x or 0X ), using abcdef or ABCDEF for 10, ...,15.

x,X unsigned int ; unsigned hexadecimal notation (without a leading 0x or 0X ), using abcdef for 0x or ABCDEF for 0X .

The GNU C Library Reference Manual, Version 2.27

‘%x’, ‘%X’ Print an integer as an unsigned hexadecimal number. ‘%x’ uses lower-case letters and ‘%X’ uses upper-case.


I am shocked- shocked- to find that integer conversions are going on here!

In C, implicit conversions abound. In Go, by design, conversions are explicit.

In C, printf implicitly converts a signed integer to unsigned for format %x. Go fmt.Printf does not.

By design, Go is not C. Go does have similarities to C, which may trap the unwary.

peterSO
  • 158,998
  • 31
  • 281
  • 276
0

int64(math.MaxUint64) overflows the range of 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)
}

Playground: https://play.golang.org/p/J6buiaaZcFt

see https://golang.org/pkg/math/#pkg-constants and https://play.golang.org/p/iul2dgRbK2E

peterSO
  • 158,998
  • 31
  • 281
  • 276