1

I find it weird that running big.NewInt(0).Bytes() returns [] instead of [0]. Is it really supposed to work that way?

https://play.golang.org/p/EEaS8sCvhFb

Toni
  • 41
  • 1
  • 4
    Not sure why exactly go does it, but it makes sense. From your playground link, you are not suprised that `Bytes()` does not return two bytes for 255. This number doesn't need to have any bits set in the second byte, so why return it as an insignificant 0, right? Similarly here, number 0 does not need to have any bits set even in the first byte. – Sergio Tulentsev Jun 09 '21 at 17:16
  • 2
    "Is it really supposed to work that way?" is a question for the package author, but seeing as it has worked that way for a decade, I wouldn't think it's incorrect behavior; particularly as calling `SetBytes([]byte{})` yields `0`, making the behavior consistent. – Adrian Jun 09 '21 at 17:26
  • {} is a common mathematical representation for 0 so this looks very natural from a set theoretic perspective. – Volker Jun 09 '21 at 20:59
  • @SergioTulentsev Bytes() doesnt return two bytes because it returns absolute value of int. – Toni Jun 10 '21 at 11:39
  • @Toni: this is a _non sequitur_. Value 255 can be represented by `00 FF` (I'm using big endian here), just as well as by `FF` – Sergio Tulentsev Jun 10 '21 at 11:42
  • @SergioTulentsev I propably misunderstood something. Sorry about that. I expect `big.NewInt(255).Bytes()` to be `FF` and not `00 FF` just like I expect `big.NewInt(-255).Bytes()` to be `FF` and not `FF 01`. – Toni Jun 10 '21 at 12:35
  • @Toni: that is my point exactly. zero bytes in insignificant positions are not needed. And in case of `big.NewInt(0)` all bytes are insignificant zeroes. – Sergio Tulentsev Jun 10 '21 at 12:38
  • @SergioTulentsev I completely understand that zero bytes in insignificant positions are not needed. Im not sure my brains can agree that all bytes in case of `big.NewInt(0).Bytes()` are insignificant but I can live with that :) And there is always `big.NewInt(0).FillBytes(...)` too. Thanks @SergioTulentsev and all the others! – Toni Jun 10 '21 at 15:32

2 Answers2

5

big.Int is a struct. It's idiomatic to make the zero value useful whenever possible. big.Int is no exception: The zero value for an Int represents the value 0.

It's an implementation detail, but the data of the Int is stored in a slice. The zero value for slices is nil, that is: no elements.

So this is very convenient, and very efficient. 0 is probably the most frequent value, and there may be cases where an initial big.Int won't get changed, and so no slice for the internal representation will be allocated.

See related: Is there another way of testing if a big.Int is 0?

icza
  • 389,944
  • 63
  • 907
  • 827
  • Its efficient if that 0 byte doesent matter. On the other hand if you need that 0 byte to be there then its inefficient to always check and handle that 0 value separately. But I quess its a tradeoff of that "efficiency". – Toni Jun 10 '21 at 11:11
2

From the documentation:

Bytes returns the absolute value of x as a big-endian byte slice.

The package API doesn't define how many bytes long the slice will be. In this case, it's using the smallest number of bytes needed to convey the whole number.

The more likely reason why this happens is an implementation detail: The big.Int maintains the bytes of the number in a slice. nil slices in Go (the zero value of a slice) have length 0. When a big.Int value is initially created, we'd expect it to also have a value of 0. Therefore, it simplifies the implementation if an empty slice internally corresponds to a numerical value of 0, without needing to perform extra checks or padding.

Hymns For Disco
  • 7,530
  • 2
  • 17
  • 33