All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD Maven / Gradle / Ivy

Go to download

International Component for Unicode for Java (ICU4J) is a mature, widely used Java library providing Unicode and Globalization support

There is a newer version: 76.1
Show newest version
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
package com.ibm.icu.impl.number;

import java.math.BigDecimal;
import java.math.BigInteger;

/**
 * A DecimalQuantity with internal storage as a 64-bit BCD, with fallback to a byte array for numbers
 * that don't fit into the standard BCD.
 */
public final class DecimalQuantity_DualStorageBCD 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".
     *
     * 

* Whenever bcd changes internally, {@link #compact()} must be called, except in special cases like * setting the digit to zero. */ private byte[] bcdBytes; private long bcdLong = 0L; private boolean usingBytes = false; @Override public int maxRepresentableDigits() { return Integer.MAX_VALUE; } public DecimalQuantity_DualStorageBCD() { setBcdToZero(); flags = 0; } public DecimalQuantity_DualStorageBCD(long input) { setToLong(input); } public DecimalQuantity_DualStorageBCD(int input) { setToInt(input); } public DecimalQuantity_DualStorageBCD(double input) { setToDouble(input); } public DecimalQuantity_DualStorageBCD(BigInteger input) { setToBigInteger(input); } public DecimalQuantity_DualStorageBCD(BigDecimal input) { setToBigDecimal(input); } public DecimalQuantity_DualStorageBCD(DecimalQuantity_DualStorageBCD other) { copyFrom(other); } public DecimalQuantity_DualStorageBCD(Number number) { // NOTE: Number type expansion happens both here // and in NumberFormat.java if (number instanceof Long) { setToLong(number.longValue()); } else if (number instanceof Integer) { setToInt(number.intValue()); } else if (number instanceof Float) { setToDouble(number.doubleValue()); } else if (number instanceof Double) { setToDouble(number.doubleValue()); } else if (number instanceof BigInteger) { setToBigInteger((BigInteger) number); } else if (number instanceof BigDecimal) { setToBigDecimal((BigDecimal) number); } else if (number instanceof com.ibm.icu.math.BigDecimal) { setToBigDecimal(((com.ibm.icu.math.BigDecimal) number).toBigDecimal()); } else { throw new IllegalArgumentException( "Number is of an unsupported type: " + number.getClass().getName()); } } @Override public DecimalQuantity createCopy() { return new DecimalQuantity_DualStorageBCD(this); } @Override protected byte getDigitPos(int position) { if (usingBytes) { if (position < 0 || position >= precision) return 0; return bcdBytes[position]; } else { if (position < 0 || position >= 16) return 0; return (byte) ((bcdLong >>> (position * 4)) & 0xf); } } @Override protected void setDigitPos(int position, byte value) { assert position >= 0; if (usingBytes) { ensureCapacity(position + 1); bcdBytes[position] = value; } else if (position >= 16) { switchStorage(); ensureCapacity(position + 1); bcdBytes[position] = value; } else { int shift = position * 4; bcdLong = bcdLong & ~(0xfL << shift) | ((long) value << shift); } } @Override protected void shiftLeft(int numDigits) { if (!usingBytes && precision + numDigits > 16) { switchStorage(); } if (usingBytes) { ensureCapacity(precision + numDigits); int i = precision + numDigits - 1; for (; i >= numDigits; i--) { bcdBytes[i] = bcdBytes[i - numDigits]; } for (; i >= 0; i--) { bcdBytes[i] = 0; } } else { bcdLong <<= (numDigits * 4); } scale -= numDigits; precision += numDigits; } @Override protected void shiftRight(int numDigits) { if (usingBytes) { int i = 0; for (; i < precision - numDigits; i++) { bcdBytes[i] = bcdBytes[i + numDigits]; } for (; i < precision; i++) { bcdBytes[i] = 0; } } else { bcdLong >>>= (numDigits * 4); } scale += numDigits; precision -= numDigits; } @Override protected void popFromLeft(int numDigits) { assert numDigits <= precision; if (usingBytes) { int i = precision - 1; for (; i >= precision - numDigits; i--) { bcdBytes[i] = 0; } } else { bcdLong &= (1L << ((precision - numDigits) * 4)) - 1; } precision -= numDigits; } @Override protected void setBcdToZero() { if (usingBytes) { bcdBytes = null; usingBytes = false; } bcdLong = 0L; scale = 0; precision = 0; isApproximate = false; origDouble = 0; origDelta = 0; } @Override protected void readIntToBcd(int n) { assert n != 0; // ints always fit inside the long implementation. long result = 0L; int i = 16; for (; n != 0; n /= 10, i--) { result = (result >>> 4) + (((long) n % 10) << 60); } assert !usingBytes; bcdLong = result >>> (i * 4); scale = 0; precision = 16 - i; } @Override protected void readLongToBcd(long n) { assert n != 0; if (n >= 10000000000000000L) { ensureCapacity(); int i = 0; for (; n != 0L; n /= 10L, i++) { bcdBytes[i] = (byte) (n % 10); } assert usingBytes; scale = 0; precision = i; } else { long result = 0L; int i = 16; for (; n != 0L; n /= 10L, i--) { result = (result >>> 4) + ((n % 10) << 60); } assert i >= 0; assert !usingBytes; bcdLong = result >>> (i * 4); scale = 0; precision = 16 - i; } } @Override protected void readBigIntegerToBcd(BigInteger n) { assert n.signum() != 0; ensureCapacity(); // allocate initial byte array int i = 0; for (; n.signum() != 0; i++) { BigInteger[] temp = n.divideAndRemainder(BigInteger.TEN); ensureCapacity(i + 1); bcdBytes[i] = temp[1].byteValue(); n = temp[0]; } scale = 0; precision = i; } @Override protected BigDecimal bcdToBigDecimal() { if (usingBytes) { // Converting to a string here is faster than doing BigInteger/BigDecimal arithmetic. BigDecimal result = new BigDecimal(toNumberString()); if (isNegative()) { result = result.negate(); } return result; } else { long tempLong = 0L; for (int shift = (precision - 1); shift >= 0; shift--) { tempLong = tempLong * 10 + getDigitPos(shift); } BigDecimal result = BigDecimal.valueOf(tempLong); // Test that the new scale fits inside the BigDecimal long newScale = result.scale() + scale; if (newScale <= Integer.MIN_VALUE) { result = BigDecimal.ZERO; } else { result = result.scaleByPowerOfTen(scale); } if (isNegative()) { result = result.negate(); } return result; } } @Override protected void compact() { if (usingBytes) { int delta = 0; for (; delta < precision && bcdBytes[delta] == 0; delta++) ; if (delta == precision) { // Number is zero setBcdToZero(); return; } else { // Remove trailing zeros shiftRight(delta); } // Compute precision int leading = precision - 1; for (; leading >= 0 && bcdBytes[leading] == 0; leading--) ; precision = leading + 1; // Switch storage mechanism if possible if (precision <= 16) { switchStorage(); } } else { if (bcdLong == 0L) { // Number is zero setBcdToZero(); return; } // Compact the number (remove trailing zeros) int delta = Long.numberOfTrailingZeros(bcdLong) / 4; bcdLong >>>= delta * 4; scale += delta; // Compute precision precision = 16 - (Long.numberOfLeadingZeros(bcdLong) / 4); } } /** Ensure that a byte array of at least 40 digits is allocated. */ private void ensureCapacity() { ensureCapacity(40); } private void ensureCapacity(int capacity) { if (capacity == 0) return; int oldCapacity = usingBytes ? bcdBytes.length : 0; if (!usingBytes) { bcdBytes = new byte[capacity]; } else if (oldCapacity < capacity) { byte[] bcd1 = new byte[capacity * 2]; System.arraycopy(bcdBytes, 0, bcd1, 0, oldCapacity); bcdBytes = bcd1; } usingBytes = true; } /** Switches the internal storage mechanism between the 64-bit long and the byte array. */ private void switchStorage() { if (usingBytes) { // Change from bytes to long bcdLong = 0L; for (int i = precision - 1; i >= 0; i--) { bcdLong <<= 4; bcdLong |= bcdBytes[i]; } bcdBytes = null; usingBytes = false; } else { // Change from long to bytes ensureCapacity(); for (int i = 0; i < precision; i++) { bcdBytes[i] = (byte) (bcdLong & 0xf); bcdLong >>>= 4; } assert usingBytes; } } @Override protected void copyBcdFrom(DecimalQuantity _other) { DecimalQuantity_DualStorageBCD other = (DecimalQuantity_DualStorageBCD) _other; setBcdToZero(); if (other.usingBytes) { ensureCapacity(other.precision); System.arraycopy(other.bcdBytes, 0, bcdBytes, 0, other.precision); } else { bcdLong = other.bcdLong; } } /** * Checks whether the bytes stored in this instance are all valid. For internal unit testing only. * * @return An error message if this instance is invalid, or null if this instance is healthy. * @internal * @deprecated This API is for ICU internal use only. */ @Deprecated public String checkHealth() { if (usingBytes) { if (bcdLong != 0) return "Value in bcdLong but we are in byte mode"; if (precision == 0) return "Zero precision but we are in byte mode"; if (precision > bcdBytes.length) return "Precision exceeds length of byte array"; if (getDigitPos(precision - 1) == 0) return "Most significant digit is zero in byte mode"; if (getDigitPos(0) == 0) return "Least significant digit is zero in long mode"; for (int i = 0; i < precision; i++) { if (getDigitPos(i) >= 10) return "Digit exceeding 10 in byte array"; if (getDigitPos(i) < 0) return "Digit below 0 in byte array"; } for (int i = precision; i < bcdBytes.length; i++) { if (getDigitPos(i) != 0) return "Nonzero digits outside of range in byte array"; } } else { if (bcdBytes != null) { for (int i = 0; i < bcdBytes.length; i++) { if (bcdBytes[i] != 0) return "Nonzero digits in byte array but we are in long mode"; } } if (precision == 0 && bcdLong != 0) return "Value in bcdLong even though precision is zero"; if (precision > 16) return "Precision exceeds length of long"; if (precision != 0 && getDigitPos(precision - 1) == 0) return "Most significant digit is zero in long mode"; if (precision != 0 && getDigitPos(0) == 0) return "Least significant digit is zero in long mode"; for (int i = 0; i < precision; i++) { if (getDigitPos(i) >= 10) return "Digit exceeding 10 in long"; if (getDigitPos(i) < 0) return "Digit below 0 in long (?!)"; } for (int i = precision; i < 16; i++) { if (getDigitPos(i) != 0) return "Nonzero digits outside of range in long"; } } return null; } /** * Checks whether this {@link DecimalQuantity_DualStorageBCD} is using its internal byte array * storage mechanism. * * @return true if an internal byte array is being used; false if a long is being used. * @internal * @deprecated This API is ICU internal only. */ @Deprecated public boolean isUsingBytes() { return usingBytes; } @Override public String toString() { return String.format("", lReqPos, rReqPos, (usingBytes ? "bytes" : "long"), (isNegative() ? "-" : ""), toNumberString()); } private String toNumberString() { StringBuilder sb = new StringBuilder(); if (usingBytes) { if (precision == 0) { sb.append('0'); } for (int i = precision - 1; i >= 0; i--) { sb.append(bcdBytes[i]); } } else { sb.append(Long.toHexString(bcdLong)); } sb.append("E"); sb.append(scale); return sb.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy