blob: 775f1795275f5bb08162bf77c66ff5a7633d0e4b [file] [log] [blame]
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
package com.ibm.icu.dev.impl.number;
import java.math.BigDecimal;
import java.math.BigInteger;
import com.ibm.icu.impl.number.DecimalQuantity;
import com.ibm.icu.impl.number.DecimalQuantity_AbstractBCD;
public final class DecimalQuantity_ByteArrayBCD extends DecimalQuantity_AbstractBCD {
/**
* The BCD of the 16 digits of the number represented by this object. Every 4 bits of the long map
* to one digit. For example, the number "12345" in BCD is "0x12345".
*
* <p>Whenever bcd changes internally, {@link #compact()} must be called, except in special cases
* like setting the digit to zero.
*/
private byte[] bcd = new byte[100];
@Override
public int maxRepresentableDigits() {
return Integer.MAX_VALUE;
}
public DecimalQuantity_ByteArrayBCD(long input) {
setToLong(input);
}
public DecimalQuantity_ByteArrayBCD(int input) {
setToInt(input);
}
public DecimalQuantity_ByteArrayBCD(double input) {
setToDouble(input);
}
public DecimalQuantity_ByteArrayBCD(BigInteger input) {
setToBigInteger(input);
}
public DecimalQuantity_ByteArrayBCD(BigDecimal input) {
setToBigDecimal(input);
}
public DecimalQuantity_ByteArrayBCD(DecimalQuantity_ByteArrayBCD other) {
copyFrom(other);
}
@Override
public DecimalQuantity createCopy() {
return new DecimalQuantity_ByteArrayBCD(this);
}
@Override
protected byte getDigitPos(int position) {
if (position < 0 || position > precision) return 0;
return bcd[position];
}
@Override
protected void setDigitPos(int position, byte value) {
assert position >= 0;
ensureCapacity(position + 1);
bcd[position] = value;
}
@Override
protected void shiftLeft(int numDigits) {
ensureCapacity(precision + numDigits);
int i = precision + numDigits - 1;
for (; i >= numDigits; i--) {
bcd[i] = bcd[i - numDigits];
}
for (; i >= 0; i--) {
bcd[i] = 0;
}
scale -= numDigits;
precision += numDigits;
}
@Override
protected void shiftRight(int numDigits) {
int i = 0;
for (; i < precision - numDigits; i++) {
bcd[i] = bcd[i + numDigits];
}
for (; i < precision; i++) {
bcd[i] = 0;
}
scale += numDigits;
precision -= numDigits;
}
@Override
protected void popFromLeft(int numDigits) {
int i = precision - 1;
for (; i >= precision - numDigits; i--) {
bcd[i] = 0;
}
precision -= numDigits;
}
@Override
protected void setBcdToZero() {
for (int i = 0; i < precision; i++) {
bcd[i] = (byte) 0;
}
scale = 0;
precision = 0;
isApproximate = false;
origDouble = 0;
origDelta = 0;
exponent = 0;
}
@Override
protected void readIntToBcd(int n) {
assert n != 0;
int i = 0;
for (; n != 0L; n /= 10L, i++) {
bcd[i] = (byte) (n % 10);
}
scale = 0;
precision = i;
}
private static final byte[] LONG_MIN_VALUE =
new byte[] {8, 0, 8, 5, 7, 7, 4, 5, 8, 6, 3, 0, 2, 7, 3, 3, 2, 2, 9};
@Override
protected void readLongToBcd(long n) {
assert n != 0;
if (n == Long.MIN_VALUE) {
// Can't consume via the normal path.
System.arraycopy(LONG_MIN_VALUE, 0, bcd, 0, LONG_MIN_VALUE.length);
scale = 0;
precision = LONG_MIN_VALUE.length;
return;
}
int i = 0;
for (; n != 0L; n /= 10L, i++) {
bcd[i] = (byte) (n % 10);
}
scale = 0;
precision = i;
}
@Override
protected void readBigIntegerToBcd(BigInteger n) {
assert n.signum() != 0;
int i = 0;
for (; n.signum() != 0; i++) {
BigInteger[] temp = n.divideAndRemainder(BigInteger.TEN);
ensureCapacity(i + 1);
bcd[i] = temp[1].byteValue();
n = temp[0];
}
scale = 0;
precision = i;
}
@Override
protected BigDecimal bcdToBigDecimal() {
// Converting to a string here is faster than doing BigInteger/BigDecimal arithmetic.
return new BigDecimal(toDumbString());
}
private String toDumbString() {
StringBuilder sb = new StringBuilder();
if (isNegative()) sb.append('-');
if (precision == 0) {
sb.append('0');
return sb.toString();
}
for (int i = precision - 1; i >= 0; i--) {
sb.append(getDigitPos(i));
}
if (scale != 0) {
sb.append('E');
sb.append(scale);
}
return sb.toString();
}
@Override
protected void compact() {
// Special handling for 0
boolean isZero = true;
for (int i = 0; i < precision; i++) {
if (bcd[i] != 0) {
isZero = false;
break;
}
}
if (isZero) {
scale = 0;
precision = 0;
return;
}
// Compact the number (remove trailing zeros)
int delta = 0;
for (; bcd[delta] == 0; delta++) ;
shiftRight(delta);
// Compute precision
int leading = precision - 1;
for (; leading >= 0 && bcd[leading] == 0; leading--) ;
precision = leading + 1;
}
private void ensureCapacity(int capacity) {
if (bcd.length >= capacity) return;
byte[] bcd1 = new byte[capacity * 2];
System.arraycopy(bcd, 0, bcd1, 0, bcd.length);
bcd = bcd1;
}
@Override
protected void copyBcdFrom(DecimalQuantity _other) {
DecimalQuantity_ByteArrayBCD other = (DecimalQuantity_ByteArrayBCD) _other;
System.arraycopy(other.bcd, 0, bcd, 0, bcd.length);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (int i = 30; i >= 0; i--) {
sb.append(bcd[i]);
}
return String.format(
"<DecimalQuantity3 %d:%d %s%s%d>",
lReqPos,
rReqPos,
sb,
"E",
scale);
}
}