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  }