mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-14 11:15:51 +00:00
CoinFormat class, for formatting and parsing coin values to and from human readable form.
This commit is contained in:
parent
384be446ce
commit
31af80ccd3
@ -20,8 +20,8 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import com.google.bitcoin.utils.CoinFormat;
|
||||
import com.google.common.math.LongMath;
|
||||
|
||||
/**
|
||||
@ -29,6 +29,12 @@ import com.google.common.math.LongMath;
|
||||
*/
|
||||
public final class Coin implements Comparable<Coin>, Serializable {
|
||||
|
||||
/**
|
||||
* Number of decimals for one Bitcoin. This constant is useful for quick adapting to other coins because a lot of
|
||||
* constants derive from it.
|
||||
*/
|
||||
public static final int NUM_COIN_DECIMALS = 8;
|
||||
|
||||
/**
|
||||
* Zero Bitcoins.
|
||||
*/
|
||||
@ -37,7 +43,7 @@ public final class Coin implements Comparable<Coin>, Serializable {
|
||||
/**
|
||||
* One Bitcoin.
|
||||
*/
|
||||
public static final Coin COIN = Coin.valueOf(100000000);
|
||||
public static final Coin COIN = Coin.valueOf(LongMath.pow(10, NUM_COIN_DECIMALS));
|
||||
|
||||
/**
|
||||
* 0.01 Bitcoins. This unit is not really used much.
|
||||
@ -154,29 +160,18 @@ public final class Coin implements Comparable<Coin>, Serializable {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
private static final CoinFormat FRIENDLY_FORMAT = CoinFormat.BTC.minDecimals(2).repeatOptionalDecimals(1, 6);
|
||||
|
||||
/**
|
||||
* Returns the value as a 0.12 type string. More digits after the decimal place will be used
|
||||
* if necessary, but two will always be present.
|
||||
*/
|
||||
public String toFriendlyString() {
|
||||
Coin value = this;
|
||||
boolean negative = value.signum() < 0;
|
||||
if (negative)
|
||||
value = value.negate();
|
||||
BigDecimal bd = new BigDecimal(BigInteger.valueOf(value.value), 8);
|
||||
String formatted = bd.toPlainString(); // Don't use scientific notation.
|
||||
int decimalPoint = formatted.indexOf(".");
|
||||
// Drop unnecessary zeros from the end.
|
||||
int toDelete = 0;
|
||||
for (int i = formatted.length() - 1; i > decimalPoint + 2; i--) {
|
||||
if (formatted.charAt(i) == '0')
|
||||
toDelete++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return (negative ? "-" : "") + formatted.substring(0, formatted.length() - toDelete);
|
||||
return FRIENDLY_FORMAT.format(this).toString();
|
||||
}
|
||||
|
||||
private static final CoinFormat PLAIN_FORMAT = CoinFormat.BTC.minDecimals(0).repeatOptionalDecimals(1, 8);
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the value as a plain string denominated in BTC.
|
||||
@ -185,8 +180,7 @@ public final class Coin implements Comparable<Coin>, Serializable {
|
||||
* </p>
|
||||
*/
|
||||
public String toPlainString() {
|
||||
BigDecimal valueInBTC = new BigDecimal(BigInteger.valueOf(value)).divide(new BigDecimal(BigInteger.valueOf(COIN.value)));
|
||||
return valueInBTC.toPlainString();
|
||||
return PLAIN_FORMAT.format(this).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
289
core/src/main/java/com/google/bitcoin/utils/CoinFormat.java
Normal file
289
core/src/main/java/com/google/bitcoin/utils/CoinFormat.java
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.bitcoin.utils;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.math.LongMath.checkedMultiply;
|
||||
import static com.google.common.math.LongMath.checkedPow;
|
||||
import static com.google.common.math.LongMath.divide;
|
||||
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.google.bitcoin.core.Coin;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Utility for formatting and parsing coin values to and from human readable form.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* CoinFormat instances are immutable. Invoking a configuration method has no effect on the receiving instance; you must
|
||||
* store and use the new instance it returns, instead. Instances are thread safe, so they may be stored safely as static
|
||||
* constants.
|
||||
* </p>
|
||||
*/
|
||||
public final class CoinFormat {
|
||||
|
||||
/** Standard format for the BTC denomination. */
|
||||
public static final CoinFormat BTC = new CoinFormat().shift(0).minDecimals(2).repeatOptionalDecimals(2, 3);
|
||||
/** Standard format for the mBTC denomination. */
|
||||
public static final CoinFormat MBTC = new CoinFormat().shift(3).minDecimals(2).optionalDecimals(2);
|
||||
/** Standard format for the µBTC denomination. */
|
||||
public static final CoinFormat UBTC = new CoinFormat().shift(6).minDecimals(0).optionalDecimals(2);
|
||||
|
||||
private final char negativeSign;
|
||||
private final char positiveSign;
|
||||
private final char decimalMark;
|
||||
private final int minDecimals;
|
||||
private final List<Integer> decimalGroups;
|
||||
private final int shift;
|
||||
private final RoundingMode roundingMode;
|
||||
|
||||
private static final String DECIMALS_PADDING = "0000000000000000"; // a few more than necessary for Bitcoin
|
||||
|
||||
/**
|
||||
* Set character to prefix negative values.
|
||||
*/
|
||||
public CoinFormat negativeSign(char negativeSign) {
|
||||
checkArgument(!Character.isDigit(negativeSign));
|
||||
checkArgument(negativeSign > 0);
|
||||
if (negativeSign == this.negativeSign)
|
||||
return this;
|
||||
else
|
||||
return new CoinFormat(negativeSign, positiveSign, decimalMark, minDecimals, decimalGroups, shift,
|
||||
roundingMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set character to prefix positive values. A zero value means no sign is used in this case. For parsing, a missing
|
||||
* sign will always be interpreted as if the positive sign was used.
|
||||
*/
|
||||
public CoinFormat positiveSign(char positiveSign) {
|
||||
checkArgument(!Character.isDigit(positiveSign));
|
||||
if (positiveSign == this.positiveSign)
|
||||
return this;
|
||||
else
|
||||
return new CoinFormat(negativeSign, positiveSign, decimalMark, minDecimals, decimalGroups, shift,
|
||||
roundingMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set character to use as the decimal mark. If the formatted value does not have any decimals, no decimal mark is
|
||||
* used either.
|
||||
*/
|
||||
public CoinFormat decimalMark(char decimalMark) {
|
||||
checkArgument(!Character.isDigit(decimalMark));
|
||||
checkArgument(decimalMark > 0);
|
||||
if (decimalMark == this.decimalMark)
|
||||
return this;
|
||||
else
|
||||
return new CoinFormat(negativeSign, positiveSign, decimalMark, minDecimals, decimalGroups, shift,
|
||||
roundingMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set minimum number of decimals to use for formatting. If the value precision exceeds all decimals specified
|
||||
* (including additional decimals specified by {@link #optionalDecimals(int...)} or
|
||||
* {@link #repeatOptionalDecimals(int, int)}), the value will be rounded. This configuration is not relevant for
|
||||
* parsing.
|
||||
*/
|
||||
public CoinFormat minDecimals(int minDecimals) {
|
||||
if (minDecimals == this.minDecimals)
|
||||
return this;
|
||||
else
|
||||
return new CoinFormat(negativeSign, positiveSign, decimalMark, minDecimals, decimalGroups, shift,
|
||||
roundingMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Set additional groups of decimals to use after the minimum decimals, if they are useful for expressing precision.
|
||||
* Each value is a number of decimals in that group. If the value precision exceeds all decimals specified
|
||||
* (including minimum decimals), the value will be rounded. This configuration is not relevant for parsing.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* For example, if you pass <tt>4,2</tt> it will add four decimals to your formatted string if needed, and then add
|
||||
* another two decimals if needed. At this point, rather than adding further decimals the value will be rounded.
|
||||
* </p>
|
||||
*
|
||||
* @param groups
|
||||
* any number numbers of decimals, one for each group
|
||||
*/
|
||||
public CoinFormat optionalDecimals(int... groups) {
|
||||
List<Integer> decimalGroups = new ArrayList<Integer>(groups.length);
|
||||
for (int group : groups)
|
||||
decimalGroups.add(group);
|
||||
return new CoinFormat(negativeSign, positiveSign, decimalMark, minDecimals, decimalGroups, shift, roundingMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Set repeated additional groups of decimals to use after the minimum decimals, if they are useful for expressing
|
||||
* precision. If the value precision exceeds all decimals specified (including minimum decimals), the value will be
|
||||
* rounded. This configuration is not relevant for parsing.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* For example, if you pass <tt>1,8</tt> it will up to eight decimals to your formatted string if needed. After
|
||||
* these have been used up, rather than adding further decimals the value will be rounded.
|
||||
* </p>
|
||||
*
|
||||
* @param decimals
|
||||
* value of the group to be repeated
|
||||
* @param repetitions
|
||||
* number of repetitions
|
||||
*/
|
||||
public CoinFormat repeatOptionalDecimals(int decimals, int repetitions) {
|
||||
checkArgument(repetitions > 0);
|
||||
List<Integer> decimalGroups = new ArrayList<Integer>(repetitions);
|
||||
for (int i = 0; i < repetitions; i++)
|
||||
decimalGroups.add(decimals);
|
||||
return new CoinFormat(negativeSign, positiveSign, decimalMark, minDecimals, decimalGroups, shift, roundingMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set number of digits to shift the decimal separator to the right, coming from the standard BTC notation that was
|
||||
* common pre-2014.
|
||||
*/
|
||||
public CoinFormat shift(int shift) {
|
||||
if (shift == this.shift)
|
||||
return this;
|
||||
else
|
||||
return new CoinFormat(negativeSign, positiveSign, decimalMark, minDecimals, decimalGroups, shift,
|
||||
roundingMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set rounding mode to use when it becomes necessary.
|
||||
*/
|
||||
public CoinFormat roundingMode(RoundingMode roundingMode) {
|
||||
if (roundingMode == this.roundingMode)
|
||||
return this;
|
||||
else
|
||||
return new CoinFormat(negativeSign, positiveSign, decimalMark, minDecimals, decimalGroups, shift,
|
||||
roundingMode);
|
||||
}
|
||||
|
||||
public CoinFormat() {
|
||||
// defaults
|
||||
this.negativeSign = '-';
|
||||
this.positiveSign = 0; // none
|
||||
this.decimalMark = '.';
|
||||
this.minDecimals = 2;
|
||||
this.decimalGroups = null;
|
||||
this.shift = 0;
|
||||
this.roundingMode = RoundingMode.HALF_UP;
|
||||
}
|
||||
|
||||
private CoinFormat(char negativeSign, char positiveSign, char decimalMark, int minDecimals,
|
||||
List<Integer> decimalGroups, int shift, RoundingMode roundingMode) {
|
||||
this.negativeSign = negativeSign;
|
||||
this.positiveSign = positiveSign;
|
||||
this.decimalMark = decimalMark;
|
||||
this.minDecimals = minDecimals;
|
||||
this.decimalGroups = decimalGroups;
|
||||
this.shift = shift;
|
||||
this.roundingMode = roundingMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the given value to a human readable form.
|
||||
*/
|
||||
public CharSequence format(Coin coin) {
|
||||
// preparation
|
||||
int maxDecimals = minDecimals;
|
||||
if (decimalGroups != null)
|
||||
for (int group : decimalGroups)
|
||||
maxDecimals += group;
|
||||
checkState(maxDecimals <= Coin.NUM_COIN_DECIMALS);
|
||||
|
||||
// rounding
|
||||
long satoshis = Math.abs(coin.value);
|
||||
long precisionDivisor = checkedPow(10, Coin.NUM_COIN_DECIMALS - shift - maxDecimals);
|
||||
satoshis = checkedMultiply(divide(satoshis, precisionDivisor, roundingMode), precisionDivisor);
|
||||
|
||||
// shifting
|
||||
long shiftDivisor = checkedPow(10, Coin.NUM_COIN_DECIMALS - shift);
|
||||
long numbers = satoshis / shiftDivisor;
|
||||
long decimals = satoshis % shiftDivisor;
|
||||
|
||||
// formatting
|
||||
String decimalsStr = String.format(Locale.US, "%0" + (Coin.NUM_COIN_DECIMALS - shift) + "d", decimals);
|
||||
StringBuilder str = new StringBuilder(decimalsStr);
|
||||
while (str.length() > minDecimals && str.charAt(str.length() - 1) == '0')
|
||||
str.setLength(str.length() - 1); // trim trailing zero
|
||||
int i = minDecimals;
|
||||
if (decimalGroups != null) {
|
||||
for (int group : decimalGroups) {
|
||||
if (str.length() > i && str.length() < i + group) {
|
||||
while (str.length() < i + group)
|
||||
str.append('0');
|
||||
break;
|
||||
}
|
||||
i += group;
|
||||
}
|
||||
}
|
||||
if (str.length() > 0)
|
||||
str.insert(0, decimalMark);
|
||||
str.insert(0, numbers);
|
||||
if (coin.value < 0)
|
||||
str.insert(0, negativeSign);
|
||||
else if (positiveSign != 0)
|
||||
str.insert(0, positiveSign);
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a human readable coin value to a {@link com.google.bitcoin.core.Coin} instance.
|
||||
*
|
||||
* @throws NumberFormatException
|
||||
* if the string cannot be parsed for some reason
|
||||
*/
|
||||
public Coin parse(String str) throws NumberFormatException {
|
||||
checkState(DECIMALS_PADDING.length() >= Coin.NUM_COIN_DECIMALS);
|
||||
if (str.isEmpty())
|
||||
throw new NumberFormatException("empty string");
|
||||
char first = str.charAt(0);
|
||||
if (first == negativeSign || first == positiveSign)
|
||||
str = str.substring(1);
|
||||
String numbers;
|
||||
String decimals;
|
||||
int decimalMarkIndex = str.indexOf(decimalMark);
|
||||
if (decimalMarkIndex != -1) {
|
||||
numbers = str.substring(0, decimalMarkIndex);
|
||||
decimals = (str + DECIMALS_PADDING).substring(decimalMarkIndex + 1);
|
||||
if (decimals.indexOf(decimalMark) != -1)
|
||||
throw new NumberFormatException("more than one decimal mark");
|
||||
} else {
|
||||
numbers = str;
|
||||
decimals = DECIMALS_PADDING;
|
||||
}
|
||||
String satoshis = numbers + decimals.substring(0, Coin.NUM_COIN_DECIMALS - shift);
|
||||
for (char c : satoshis.toCharArray())
|
||||
if (!Character.isDigit(c))
|
||||
throw new NumberFormatException("illegal character: " + c);
|
||||
Coin coin = Coin.valueOf(Long.parseLong(satoshis));
|
||||
if (first == negativeSign)
|
||||
coin = coin.negate();
|
||||
return coin;
|
||||
}
|
||||
}
|
259
core/src/test/java/com/google/bitcoin/utils/CoinFormatTest.java
Normal file
259
core/src/test/java/com/google/bitcoin/utils/CoinFormatTest.java
Normal file
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.bitcoin.utils;
|
||||
|
||||
import static com.google.bitcoin.core.Coin.COIN;
|
||||
import static com.google.bitcoin.core.Coin.SATOSHI;
|
||||
import static com.google.bitcoin.core.Coin.ZERO;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.bitcoin.core.Coin;
|
||||
|
||||
public class CoinFormatTest {
|
||||
|
||||
@Test
|
||||
public void testSigns() throws Exception {
|
||||
assertEquals("-1.00", CoinFormat.BTC.format(Coin.COIN.negate()).toString());
|
||||
assertEquals("@1.00", CoinFormat.BTC.negativeSign('@').format(Coin.COIN.negate()).toString());
|
||||
assertEquals("1.00", CoinFormat.BTC.format(Coin.COIN).toString());
|
||||
assertEquals("+1.00", CoinFormat.BTC.positiveSign('+').format(Coin.COIN).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecimalMark() throws Exception {
|
||||
assertEquals("1.00", CoinFormat.BTC.format(Coin.COIN).toString());
|
||||
assertEquals("1,00", CoinFormat.BTC.decimalMark(',').format(Coin.COIN).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGrouping() throws Exception {
|
||||
assertEquals("0.1", format(Coin.parseCoin("0.1"), 0, 1, 2, 3));
|
||||
assertEquals("0.010", format(Coin.parseCoin("0.01"), 0, 1, 2, 3));
|
||||
assertEquals("0.001", format(Coin.parseCoin("0.001"), 0, 1, 2, 3));
|
||||
assertEquals("0.000100", format(Coin.parseCoin("0.0001"), 0, 1, 2, 3));
|
||||
assertEquals("0.000010", format(Coin.parseCoin("0.00001"), 0, 1, 2, 3));
|
||||
assertEquals("0.000001", format(Coin.parseCoin("0.000001"), 0, 1, 2, 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void btcRounding() throws Exception {
|
||||
assertEquals("0", format(ZERO, 0, 0));
|
||||
assertEquals("0.00", format(ZERO, 0, 2));
|
||||
|
||||
assertEquals("1", format(COIN, 0, 0));
|
||||
assertEquals("1.0", format(COIN, 0, 1));
|
||||
assertEquals("1.00", format(COIN, 0, 2, 2));
|
||||
assertEquals("1.00", format(COIN, 0, 2, 2, 2));
|
||||
assertEquals("1.00", format(COIN, 0, 2, 2, 2, 2));
|
||||
assertEquals("1.000", format(COIN, 0, 3));
|
||||
assertEquals("1.0000", format(COIN, 0, 4));
|
||||
|
||||
final Coin justNot = COIN.subtract(SATOSHI);
|
||||
assertEquals("1", format(justNot, 0, 0));
|
||||
assertEquals("1.0", format(justNot, 0, 1));
|
||||
assertEquals("1.00", format(justNot, 0, 2, 2));
|
||||
assertEquals("1.00", format(justNot, 0, 2, 2, 2));
|
||||
assertEquals("0.99999999", format(justNot, 0, 2, 2, 2, 2));
|
||||
assertEquals("1.000", format(justNot, 0, 3));
|
||||
assertEquals("1.0000", format(justNot, 0, 4));
|
||||
|
||||
final Coin slightlyMore = COIN.add(SATOSHI);
|
||||
assertEquals("1", format(slightlyMore, 0, 0));
|
||||
assertEquals("1.0", format(slightlyMore, 0, 1));
|
||||
assertEquals("1.00", format(slightlyMore, 0, 2, 2));
|
||||
assertEquals("1.00", format(slightlyMore, 0, 2, 2, 2));
|
||||
assertEquals("1.00000001", format(slightlyMore, 0, 2, 2, 2, 2));
|
||||
assertEquals("1.000", format(slightlyMore, 0, 3));
|
||||
assertEquals("1.0000", format(slightlyMore, 0, 4));
|
||||
|
||||
final Coin pivot = COIN.add(SATOSHI.multiply(5));
|
||||
assertEquals("1.00000005", format(pivot, 0, 8));
|
||||
assertEquals("1.00000005", format(pivot, 0, 7, 1));
|
||||
assertEquals("1.0000001", format(pivot, 0, 7));
|
||||
|
||||
final Coin value = Coin.valueOf(1122334455667788l);
|
||||
assertEquals("11223345", format(value, 0, 0));
|
||||
assertEquals("11223344.6", format(value, 0, 1));
|
||||
assertEquals("11223344.5567", format(value, 0, 2, 2));
|
||||
assertEquals("11223344.556678", format(value, 0, 2, 2, 2));
|
||||
assertEquals("11223344.55667788", format(value, 0, 2, 2, 2, 2));
|
||||
assertEquals("11223344.557", format(value, 0, 3));
|
||||
assertEquals("11223344.5567", format(value, 0, 4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mBtcRounding() throws Exception {
|
||||
assertEquals("0", format(ZERO, 3, 0));
|
||||
assertEquals("0.00", format(ZERO, 3, 2));
|
||||
|
||||
assertEquals("1000", format(COIN, 3, 0));
|
||||
assertEquals("1000.0", format(COIN, 3, 1));
|
||||
assertEquals("1000.00", format(COIN, 3, 2));
|
||||
assertEquals("1000.00", format(COIN, 3, 2, 2));
|
||||
assertEquals("1000.000", format(COIN, 3, 3));
|
||||
assertEquals("1000.0000", format(COIN, 3, 4));
|
||||
|
||||
final Coin justNot = COIN.subtract(SATOSHI.multiply(10));
|
||||
assertEquals("1000", format(justNot, 3, 0));
|
||||
assertEquals("1000.0", format(justNot, 3, 1));
|
||||
assertEquals("1000.00", format(justNot, 3, 2));
|
||||
assertEquals("999.9999", format(justNot, 3, 2, 2));
|
||||
assertEquals("1000.000", format(justNot, 3, 3));
|
||||
assertEquals("999.9999", format(justNot, 3, 4));
|
||||
|
||||
final Coin slightlyMore = COIN.add(SATOSHI.multiply(10));
|
||||
assertEquals("1000", format(slightlyMore, 3, 0));
|
||||
assertEquals("1000.0", format(slightlyMore, 3, 1));
|
||||
assertEquals("1000.00", format(slightlyMore, 3, 2));
|
||||
assertEquals("1000.000", format(slightlyMore, 3, 3));
|
||||
assertEquals("1000.0001", format(slightlyMore, 3, 2, 2));
|
||||
assertEquals("1000.0001", format(slightlyMore, 3, 4));
|
||||
|
||||
final Coin pivot = COIN.add(SATOSHI.multiply(50));
|
||||
assertEquals("1000.0005", format(pivot, 3, 4));
|
||||
assertEquals("1000.0005", format(pivot, 3, 3, 1));
|
||||
assertEquals("1000.001", format(pivot, 3, 3));
|
||||
|
||||
final Coin value = Coin.valueOf(1122334455667788l);
|
||||
assertEquals("11223344557", format(value, 3, 0));
|
||||
assertEquals("11223344556.7", format(value, 3, 1));
|
||||
assertEquals("11223344556.68", format(value, 3, 2));
|
||||
assertEquals("11223344556.6779", format(value, 3, 2, 2));
|
||||
assertEquals("11223344556.678", format(value, 3, 3));
|
||||
assertEquals("11223344556.6779", format(value, 3, 4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uBtcRounding() throws Exception {
|
||||
assertEquals("0", format(ZERO, 6, 0));
|
||||
assertEquals("0.00", format(ZERO, 6, 2));
|
||||
|
||||
assertEquals("1000000", format(COIN, 6, 0));
|
||||
assertEquals("1000000", format(COIN, 6, 0, 2));
|
||||
assertEquals("1000000.0", format(COIN, 6, 1));
|
||||
assertEquals("1000000.00", format(COIN, 6, 2));
|
||||
|
||||
final Coin justNot = COIN.subtract(SATOSHI);
|
||||
assertEquals("1000000", format(justNot, 6, 0));
|
||||
assertEquals("999999.99", format(justNot, 6, 0, 2));
|
||||
assertEquals("1000000.0", format(justNot, 6, 1));
|
||||
assertEquals("999999.99", format(justNot, 6, 2));
|
||||
|
||||
final Coin slightlyMore = COIN.add(SATOSHI);
|
||||
assertEquals("1000000", format(slightlyMore, 6, 0));
|
||||
assertEquals("1000000.01", format(slightlyMore, 6, 0, 2));
|
||||
assertEquals("1000000.0", format(slightlyMore, 6, 1));
|
||||
assertEquals("1000000.01", format(slightlyMore, 6, 2));
|
||||
|
||||
final Coin pivot = COIN.add(SATOSHI.multiply(5));
|
||||
assertEquals("1000000.05", format(pivot, 6, 2));
|
||||
assertEquals("1000000.05", format(pivot, 6, 0, 2));
|
||||
assertEquals("1000000.1", format(pivot, 6, 1));
|
||||
assertEquals("1000000.1", format(pivot, 6, 0, 1));
|
||||
|
||||
final Coin value = Coin.valueOf(1122334455667788l);
|
||||
assertEquals("11223344556678", format(value, 6, 0));
|
||||
assertEquals("11223344556677.88", format(value, 6, 2));
|
||||
assertEquals("11223344556677.9", format(value, 6, 1));
|
||||
assertEquals("11223344556677.88", format(value, 6, 2));
|
||||
}
|
||||
|
||||
private String format(Coin coin, int shift, int minDecimals, int... decimalGroups) {
|
||||
return new CoinFormat().shift(shift).minDecimals(minDecimals).optionalDecimals(decimalGroups).format(coin)
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse() throws Exception {
|
||||
assertEquals(Coin.COIN, CoinFormat.BTC.parse("1"));
|
||||
assertEquals(Coin.COIN, CoinFormat.BTC.parse("1."));
|
||||
assertEquals(Coin.COIN, CoinFormat.BTC.parse("1.0"));
|
||||
assertEquals(Coin.COIN, CoinFormat.BTC.decimalMark(',').parse("1,0"));
|
||||
assertEquals(Coin.COIN, CoinFormat.BTC.parse("01.0000000000"));
|
||||
assertEquals(Coin.COIN, CoinFormat.BTC.positiveSign('+').parse("+1.0"));
|
||||
assertEquals(Coin.COIN.negate(), CoinFormat.BTC.parse("-1"));
|
||||
assertEquals(Coin.COIN.negate(), CoinFormat.BTC.parse("-1.0"));
|
||||
|
||||
assertEquals(Coin.CENT, CoinFormat.BTC.parse(".01"));
|
||||
|
||||
assertEquals(Coin.MILLICOIN, CoinFormat.MBTC.parse("1"));
|
||||
assertEquals(Coin.MILLICOIN, CoinFormat.MBTC.parse("1.0"));
|
||||
assertEquals(Coin.MILLICOIN, CoinFormat.MBTC.parse("01.0000000000"));
|
||||
assertEquals(Coin.MILLICOIN, CoinFormat.MBTC.positiveSign('+').parse("+1.0"));
|
||||
assertEquals(Coin.MILLICOIN.negate(), CoinFormat.MBTC.parse("-1"));
|
||||
assertEquals(Coin.MILLICOIN.negate(), CoinFormat.MBTC.parse("-1.0"));
|
||||
|
||||
assertEquals(Coin.MICROCOIN, CoinFormat.UBTC.parse("1"));
|
||||
assertEquals(Coin.MICROCOIN, CoinFormat.UBTC.parse("1.0"));
|
||||
assertEquals(Coin.MICROCOIN, CoinFormat.UBTC.parse("01.0000000000"));
|
||||
assertEquals(Coin.MICROCOIN, CoinFormat.UBTC.positiveSign('+').parse("+1.0"));
|
||||
assertEquals(Coin.MICROCOIN.negate(), CoinFormat.UBTC.parse("-1"));
|
||||
assertEquals(Coin.MICROCOIN.negate(), CoinFormat.UBTC.parse("-1.0"));
|
||||
}
|
||||
|
||||
@Test(expected = NumberFormatException.class)
|
||||
public void parseInvalidEmpty() throws Exception {
|
||||
CoinFormat.BTC.parse("");
|
||||
}
|
||||
|
||||
@Test(expected = NumberFormatException.class)
|
||||
public void parseInvalidWhitespaceBefore() throws Exception {
|
||||
CoinFormat.BTC.parse(" 1");
|
||||
}
|
||||
|
||||
@Test(expected = NumberFormatException.class)
|
||||
public void parseInvalidWhitespaceSign() throws Exception {
|
||||
CoinFormat.BTC.parse("- 1");
|
||||
}
|
||||
|
||||
@Test(expected = NumberFormatException.class)
|
||||
public void parseInvalidWhitespaceAfter() throws Exception {
|
||||
CoinFormat.BTC.parse("1 ");
|
||||
}
|
||||
|
||||
@Test(expected = NumberFormatException.class)
|
||||
public void parseInvalidMultipleDecimalMarks() throws Exception {
|
||||
CoinFormat.BTC.parse("1.0.0");
|
||||
}
|
||||
|
||||
@Test(expected = NumberFormatException.class)
|
||||
public void parseInvalidDecimalMark() throws Exception {
|
||||
CoinFormat.BTC.decimalMark(',').parse("1.0");
|
||||
}
|
||||
|
||||
@Test(expected = NumberFormatException.class)
|
||||
public void parseInvalidPositiveSign() throws Exception {
|
||||
CoinFormat.BTC.positiveSign('@').parse("+1.0");
|
||||
}
|
||||
|
||||
@Test(expected = NumberFormatException.class)
|
||||
public void parseInvalidNegativeSign() throws Exception {
|
||||
CoinFormat.BTC.negativeSign('@').parse("-1.0");
|
||||
}
|
||||
|
||||
@Test(expected = NumberFormatException.class)
|
||||
public void parseInvalidHugeNumber() throws Exception {
|
||||
System.out.println(CoinFormat.BTC.parse("99999999999999999999"));
|
||||
}
|
||||
|
||||
@Test(expected = NumberFormatException.class)
|
||||
public void parseInvalidHugeNegativeNumber() throws Exception {
|
||||
System.out.println(CoinFormat.BTC.parse("-99999999999999999999"));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user