bits.go raw

   1  package bits
   2  
   3  import "math/big"
   4  
   5  // CompactToBig converts a compact representation of a whole number N to an unsigned 32-bit number. The representation
   6  // is similar to IEEE754 floating point numbers. Like IEEE754 floating point, there are three basic components: the
   7  // sign, the exponent, and the mantissa. They are broken out as follows:
   8  //
   9  //	* the most significant 8 bits represent the unsigned base 256 exponent
  10  // 	* bit 23 (the 24th bit) represents the sign bit
  11  //	* the least significant 23 bits represent the mantissa
  12  //	-------------------------------------------------
  13  //	|   Exponent     |    Sign    |    Mantissa     |
  14  //	-------------------------------------------------
  15  //	| 8 bits [31-24] | 1 bit [23] | 23 bits [22-00] |
  16  //	-------------------------------------------------
  17  //
  18  // The formula to calculate N is:
  19  //
  20  // 	N = (-1^sign) * mantissa * 256^(exponent-3)
  21  //
  22  // This compact form is only used in bitcoin to encode unsigned 256-bit numbers which represent difficulty targets, thus
  23  // there really is not a need for a sign bit, but it is implemented here to stay consistent with bitcoind.
  24  func CompactToBig(compact uint32) *big.Int {
  25  	// Extract the mantissa, sign bit, and exponent.
  26  	mantissa := compact & 0x007fffff
  27  	isNegative := compact&0x00800000 != 0
  28  	exponent := uint(compact >> 24)
  29  	// Since the base for the exponent is 256, the exponent can be treated as the number of bytes to represent the full
  30  	// 256-bit number. So, treat the exponent as the number of bytes and shift the mantissa right or left accordingly.
  31  	// This is equivalent to N = mantissa * 256^(exponent-3)
  32  	var bn *big.Int
  33  	if exponent <= 3 {
  34  		mantissa >>= 8 * (3 - exponent)
  35  		bn = big.NewInt(int64(mantissa))
  36  	} else {
  37  		bn = big.NewInt(int64(mantissa))
  38  		bn.Lsh(bn, 8*(exponent-3))
  39  	}
  40  	// Make it negative if the sign bit is set.
  41  	if isNegative {
  42  		bn = bn.Neg(bn)
  43  	}
  44  	return bn
  45  }
  46  
  47  // BigToCompact converts a whole number N to a compact representation using an unsigned 32-bit number. The compact
  48  // representation only provides 23 bits of precision, so values larger than (2^23 - 1) only encode the most significant
  49  // digits of the number. See CompactToBig for details.
  50  func BigToCompact(n *big.Int) uint32 {
  51  	// No need to do any work if it's zero.
  52  	if n.Sign() == 0 {
  53  		return 0
  54  	}
  55  	// Since the base for the exponent is 256, the exponent can be treated as the number of bytes. So, shift the number
  56  	// right or left accordingly. This is equivalent to: mantissa = mantissa / 256^(exponent-3)
  57  	var mantissa uint32
  58  	exponent := uint(len(n.Bytes()))
  59  	if exponent <= 3 {
  60  		mantissa = uint32(n.Bits()[0])
  61  		mantissa <<= 8 * (3 - exponent)
  62  	} else {
  63  		// Use a copy to avoid modifying the caller's original number.
  64  		tn := new(big.Int).Set(n)
  65  		mantissa = uint32(tn.Rsh(tn, 8*(exponent-3)).Bits()[0])
  66  	}
  67  	// When the mantissa already has the sign bit set, the number is too large to fit into the available 23-bits, so
  68  	// divide the number by 256 and increment the exponent accordingly.
  69  	if mantissa&0x00800000 != 0 {
  70  		mantissa >>= 8
  71  		exponent++
  72  	}
  73  	// Pack the exponent, sign bit, and mantissa into an unsigned 32-bit int and return it.
  74  	compact := uint32(exponent<<24) | mantissa
  75  	if n.Sign() < 0 {
  76  		compact |= 0x00800000
  77  	}
  78  	return compact
  79  }
  80