Add Amount.MulF64.
ok @davecgh, @jcvernaleo
This commit is contained in:
parent
fb4c64910d
commit
2847c14f06
3 changed files with 133 additions and 11 deletions
30
amount.go
30
amount.go
|
@ -53,6 +53,17 @@ func (u AmountUnit) String() string {
|
||||||
// to as a `Satoshi'). A single Amount is equal to 1e-8 of a bitcoin.
|
// to as a `Satoshi'). A single Amount is equal to 1e-8 of a bitcoin.
|
||||||
type Amount int64
|
type Amount int64
|
||||||
|
|
||||||
|
// round converts a floating point number, which may or may not be representable
|
||||||
|
// as an integer, to the Amount integer type by rounding to the nearest integer.
|
||||||
|
// This is performed by adding or subtracting 0.5 depending on the sign, and
|
||||||
|
// relying on integer truncation to round the value to the nearest Amount.
|
||||||
|
func round(f float64) Amount {
|
||||||
|
if f < 0 {
|
||||||
|
return Amount(f - 0.5)
|
||||||
|
}
|
||||||
|
return Amount(f + 0.5)
|
||||||
|
}
|
||||||
|
|
||||||
// NewAmount creates an Amount from a floating point value representing
|
// NewAmount creates an Amount from a floating point value representing
|
||||||
// some value in bitcoin. NewAmount errors if f is NaN or +-Infinity, but
|
// some value in bitcoin. NewAmount errors if f is NaN or +-Infinity, but
|
||||||
// does not check that the amount is within the total amount of bitcoin
|
// does not check that the amount is within the total amount of bitcoin
|
||||||
|
@ -69,16 +80,7 @@ func NewAmount(f float64) (Amount, error) {
|
||||||
return 0, errors.New("invalid bitcoin amount")
|
return 0, errors.New("invalid bitcoin amount")
|
||||||
}
|
}
|
||||||
|
|
||||||
a := f * float64(SatoshiPerBitcoin)
|
return round(f * satoshiPerBitcoin), nil
|
||||||
|
|
||||||
// Depending on the sign, add or subtract 0.5 and rely on integer
|
|
||||||
// truncation to correctly round the value up or down.
|
|
||||||
if a < 0 {
|
|
||||||
a = a - 0.5
|
|
||||||
} else {
|
|
||||||
a = a + 0.5
|
|
||||||
}
|
|
||||||
return Amount(a), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToUnit converts a monetary amount counted in bitcoin base units to a
|
// ToUnit converts a monetary amount counted in bitcoin base units to a
|
||||||
|
@ -100,3 +102,11 @@ func (a Amount) Format(u AmountUnit) string {
|
||||||
func (a Amount) String() string {
|
func (a Amount) String() string {
|
||||||
return a.Format(AmountBTC)
|
return a.Format(AmountBTC)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MulF64 multiplies an Amount by a floating point value. While this is not
|
||||||
|
// an operation that must typically be done by a full node or wallet, it is
|
||||||
|
// useful for services that build on top of bitcoin (for example, calculating
|
||||||
|
// a fee by multiplying by a percentage).
|
||||||
|
func (a Amount) MulF64(f float64) Amount {
|
||||||
|
return round(float64(a) * f)
|
||||||
|
}
|
||||||
|
|
107
amount_test.go
107
amount_test.go
|
@ -192,3 +192,110 @@ func TestAmountUnitConversions(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAmountMulF64(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
amt Amount
|
||||||
|
mul float64
|
||||||
|
res Amount
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Multiply 0.1 BTC by 2",
|
||||||
|
amt: 100e5, // 0.1 BTC
|
||||||
|
mul: 2,
|
||||||
|
res: 200e5, // 0.2 BTC
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiply 0.2 BTC by 0.02",
|
||||||
|
amt: 200e5, // 0.2 BTC
|
||||||
|
mul: 1.02,
|
||||||
|
res: 204e5, // 0.204 BTC
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiply 0.1 BTC by -2",
|
||||||
|
amt: 100e5, // 0.1 BTC
|
||||||
|
mul: -2,
|
||||||
|
res: -200e5, // -0.2 BTC
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiply 0.2 BTC by -0.02",
|
||||||
|
amt: 200e5, // 0.2 BTC
|
||||||
|
mul: -1.02,
|
||||||
|
res: -204e5, // -0.204 BTC
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiply -0.1 BTC by 2",
|
||||||
|
amt: -100e5, // -0.1 BTC
|
||||||
|
mul: 2,
|
||||||
|
res: -200e5, // -0.2 BTC
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiply -0.2 BTC by 0.02",
|
||||||
|
amt: -200e5, // -0.2 BTC
|
||||||
|
mul: 1.02,
|
||||||
|
res: -204e5, // -0.204 BTC
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiply -0.1 BTC by -2",
|
||||||
|
amt: -100e5, // -0.1 BTC
|
||||||
|
mul: -2,
|
||||||
|
res: 200e5, // 0.2 BTC
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiply -0.2 BTC by -0.02",
|
||||||
|
amt: -200e5, // -0.2 BTC
|
||||||
|
mul: -1.02,
|
||||||
|
res: 204e5, // 0.204 BTC
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Round down",
|
||||||
|
amt: 49, // 49 Satoshis
|
||||||
|
mul: 0.01,
|
||||||
|
res: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Round up",
|
||||||
|
amt: 50, // 50 Satoshis
|
||||||
|
mul: 0.01,
|
||||||
|
res: 1, // 1 Satoshi
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiply by 0.",
|
||||||
|
amt: 1e8, // 1 BTC
|
||||||
|
mul: 0,
|
||||||
|
res: 0, // 0 BTC
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiply 1 by 0.5.",
|
||||||
|
amt: 1, // 1 Satoshi
|
||||||
|
mul: 0.5,
|
||||||
|
res: 1, // 1 Satoshi
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiply 100 by 66%.",
|
||||||
|
amt: 100, // 100 Satoshis
|
||||||
|
mul: 0.66,
|
||||||
|
res: 66, // 66 Satoshis
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiply 100 by 66.6%.",
|
||||||
|
amt: 100, // 100 Satoshis
|
||||||
|
mul: 0.666,
|
||||||
|
res: 67, // 67 Satoshis
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiply 100 by 2/3.",
|
||||||
|
amt: 100, // 100 Satoshis
|
||||||
|
mul: 2.0 / 3,
|
||||||
|
res: 67, // 67 Satoshis
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
a := test.amt.MulF64(test.mul)
|
||||||
|
if a != test.res {
|
||||||
|
t.Errorf("%v: expected %v got %v", test.name, test.res, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
7
const.go
7
const.go
|
@ -5,11 +5,16 @@
|
||||||
package btcutil
|
package btcutil
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// satoshiPerBitcoin is the untyped version of SatoshiPerBitcoin.
|
||||||
|
//
|
||||||
|
// TODO(jrick): Switch the exported consts below to be untyped.
|
||||||
|
satoshiPerBitcoin = 1e8
|
||||||
|
|
||||||
// SatoshiPerBitcent is the number of satoshi in one bitcoin cent.
|
// SatoshiPerBitcent is the number of satoshi in one bitcoin cent.
|
||||||
SatoshiPerBitcent int64 = 1e6
|
SatoshiPerBitcent int64 = 1e6
|
||||||
|
|
||||||
// SatoshiPerBitcoin is the number of satoshi in one bitcoin (1 BTC).
|
// SatoshiPerBitcoin is the number of satoshi in one bitcoin (1 BTC).
|
||||||
SatoshiPerBitcoin int64 = 1e8
|
SatoshiPerBitcoin int64 = satoshiPerBitcoin
|
||||||
|
|
||||||
// MaxSatoshi is the maximum transaction amount allowed in satoshi.
|
// MaxSatoshi is the maximum transaction amount allowed in satoshi.
|
||||||
MaxSatoshi int64 = 21e6 * SatoshiPerBitcoin
|
MaxSatoshi int64 = 21e6 * SatoshiPerBitcoin
|
||||||
|
|
Loading…
Reference in a new issue