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