com.ibm.as400.access.AS400DecFloat Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jt400 Show documentation
Show all versions of jt400 Show documentation
The Open Source version of the IBM Toolbox for Java
The newest version!
///////////////////////////////////////////////////////////////////////////////
//
// JTOpen (IBM Toolbox for Java - OSS version)
//
// Filename: AS400DecFloat.java
//
// The source code contained herein is licensed under the IBM Public License
// Version 1.0, which has been approved by the Open Source Initiative.
// Copyright (C) 2006 International Business Machines Corporation and
// others. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////
package com.ibm.as400.access;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.BigInteger;
//@DFA new class
/**
* The AS400DecFloat class provides a converter between a BigDecimal object and a DecimalFloat type.
**/
public class AS400DecFloat implements AS400DataType
{
static final long serialVersionUID = 4L;
private int digits; //Precision 16 or 34
private static final long defaultValue = 0;
static final boolean HIGH_NIBBLE = true;
static final boolean LOW_NIBBLE = false;
private final static int DEC_FLOAT_16_BIAS = 398;
private final static long DEC_FLOAT_16_SIGNAL_MASK = 0x0200000000000000L; // 1 bit (7th bit from left) //@snan
private final static long DEC_FLOAT_16_SIGN_MASK = 0x8000000000000000L; // 1 bits
private final static long DEC_FLOAT_16_COMBINATION_MASK = 0x7c00000000000000L; // 5 bits
private final static long DEC_FLOAT_16_EXPONENT_CONTINUATION_MASK = 0x03fc000000000000L; // 8 bits
private final static long DEC_FLOAT_16_COEFFICIENT_CONTINUATION_MASK = 0x0003ffffffffffffL; // 50 bits
private final static int DEC_FLOAT_34_BIAS = 6176;
private final static long DEC_FLOAT_34_SIGNAL_MASK = 0x0200000000000000L; // 1 bit (7th bit from left) //@snan
private final static long DEC_FLOAT_34_SIGN_MASK = 0x8000000000000000L; // 1 bits
private final static long DEC_FLOAT_34_COMBINATION_MASK = 0x7c00000000000000L; // 5 bits
private final static long DEC_FLOAT_34_EXPONENT_CONTINUATION_MASK = 0x03ffc00000000000L; // 12 bits
final static long DEC_FLOAT_34_COEFFICIENT_CONTINUATION_MASK = 0x00003fffffffffffL; // 46 bits + 64 bits = 110 bits
private static final int[][] tenRadixMagnitude = { { 0x3b9aca00 }, // 10^9
{ 0x0de0b6b3, 0xa7640000 }, // 10^18
{ 0x033b2e3c, 0x9fd0803c, 0xe8000000 }, // 10^27
};
/**
* Constructs an AS400DecFloat object.
* @param numDigits The number of digits (16 or 34).
**/
public AS400DecFloat(int numDigits)
{
// check for valid input
if (numDigits != 16 && numDigits != 34) //34 is max for DecFloat(34)
{
throw new ExtendedIllegalArgumentException("numDigits (" + String.valueOf(numDigits) + ")", ExtendedIllegalArgumentException.RANGE_NOT_VALID);
}
// set instance variables
this.digits = numDigits;
}
/**
* Creates a new AS400DecFloat object that is identical to the current instance.
* @return The new object.
**/
public Object clone()
{
try
{
return super.clone(); // Object.clone does not throw exception
}
catch (CloneNotSupportedException e)
{
Trace.log(Trace.ERROR, "Unexpected cloning error", e);
throw new InternalErrorException(InternalErrorException.UNKNOWN, e);
}
}
/**
* Returns the byte length of the data type.
* @return The number of bytes in the server representation of the data type.
**/
public int getByteLength()
{
return digits == 16 ? 8 : 16; //either 8 or 16 bytes on server
}
/**
* Returns a Java object representing the default value of the data type.
* @return The BigDecimal object with a value of zero.
**/
public Object getDefaultValue()
{
return BigDecimal.valueOf(defaultValue);
}
/**
* Returns instance type
* @return AS400DataType.TYPE_DECFLOAT.
**/
public int getInstanceType()
{
return AS400DataType.TYPE_DECFLOAT;
}
/**
* Returns the Java class that corresponds with this data type.
* @return BigDecimal.class.
**/
public Class getJavaType()
{
return BigDecimal.class;
}
/**
* Returns the total number of digits in the decfloat number.
* @return The number of digits.
**/
public int getNumberOfDigits()
{
return this.digits;
}
/**
* Converts the specified Java object to server format.
* @param javaValue The object corresponding to the data type. It must be an instance of BigDecimal and the BigDecimal must have a less than or equal to number of digits.
* @return The server representation of the data type.
**/
public byte[] toBytes(Object javaValue)
{
byte[] as400Value = new byte[this.getByteLength()];
this.toBytes(javaValue, as400Value, 0);
return as400Value;
}
/**
* Converts the specified Java object into server format in the specified byte array.
* @param javaValue The object corresponding to the data type. It must be an instance of BigDecimal and the BigDecimal must have a less than or equal to number of digits.
* @param as400Value The array to receive the data type in server format. There must be enough space to hold the server value.
* @return The number of bytes in the server representation of the data type.
**/
public int toBytes(Object javaValue, byte[] as400Value)
{
return this.toBytes(javaValue, as400Value, 0);
}
/**
* Converts the specified Java object into server format in the specified byte array.
* @param javaValue An object corresponding to the data type. It must be an instance of BigDecimal or String (if value is "NaN", "Infinity", or "-Infinity").
* @param as400Value The array to receive the data type in server format. There must be enough space to hold the server value.
* @param offset The offset into the byte array for the start of the server value. It must be greater than or equal to zero.
* @return The number of bytes in the server representation of the data type.
**/
public int toBytes(Object javaValue, byte[] as400Value, int offset)
{
//verify input
long specialCombination = 0L; //ieee algorithm says: Combination G (11111-> NaN, 11110-> (-1)^sign Infinity)
int signalingNaN = -1; //@sig1 for now, only support non-signaling until decfloat/double etc support it
if(javaValue instanceof String)
{
//special value "NaN", "Infinity", or "-Infinity"
//use dummy BigDecimal("1" or "-1"), and overlay ieee (Combination G) at end of method
if ( javaValue.equals("NaN") )
{
javaValue = new BigDecimal("1");
specialCombination = 0x1fL;
signalingNaN = 0; //@sig1 non signaling
}
else if ( javaValue.equals("-NaN") )
{
javaValue = new BigDecimal("-1");
specialCombination = 0x1fL;
signalingNaN = 0; //@sig1 non signaling
}
else if ( javaValue.equals("SNaN") ) //@snan
{
javaValue = new BigDecimal("1");
specialCombination = 0x1fL;
signalingNaN = 1; //@sig1 signaling
}
else if ( javaValue.equals("-SNaN") ) //@snan
{
javaValue = new BigDecimal("-1");
specialCombination = 0x1fL;
signalingNaN = 1; //@sig1 signaling
}
else if ( javaValue.equals("Infinity") )
{
javaValue = new BigDecimal("1");
specialCombination = 0x1eL;
}
else if ( javaValue.equals("-Infinity") )
{
javaValue = new BigDecimal("-1"); //negative dummy so that sign gets set to negative Infinity
specialCombination = 0x1eL;
}
}
BigDecimal inValue = (BigDecimal) javaValue; // Let this line throw ClassCastException
//get the sign of the BigDecimal.
int sign = inValue.signum ();
//get the exponent.
long exponent = inValue.scale () * (-1);
//get the unscaled value as string.
String bdUnscaledStr = inValue.abs().unscaledValue().toString ();
if (this.digits == 16) //DECFLOAT16
{
//get precision of the BigDecimal.
int bdPrecision = SQLDataFactory.getPrecisionForTruncation(inValue, 16)[0]; //bdUnscaledStr.length (); //@rnd1
//bug in jdk1.5 (need to pad with 0z if exp is greater than (maxexp - (maxprecision - precision)) (ie. 9.99E380 since 380>384-(16-3)) //@max
//so if among the top 16 exponent values, then need to have the precision digits padded with zeros
int zeros = 0; //@max
if((exponent > 368 ) && bdUnscaledStr.length() < 16){ //maxexp-16 //@max
//pad 0s //@max
zeros = 16 - bdUnscaledStr.length(); //@max
bdUnscaledStr += "0000000000000000"; //@max
bdUnscaledStr = bdUnscaledStr.substring(0, 16); //@max
bdPrecision += zeros; //@max
exponent -= zeros; //@max
} //@max
if(bdUnscaledStr.length() > bdPrecision)
exponent = bdUnscaledStr.length() - bdPrecision; //get exponent in terms of precisionForTruncation
// check for error condition.
if ((exponent + (bdPrecision - 1)) > 384)
throw new ExtendedIllegalArgumentException("numDecimalPositions (" + String.valueOf((exponent + (bdPrecision - 1))) + ")", ExtendedIllegalArgumentException.RANGE_NOT_VALID);
else if ((exponent + (bdPrecision - 1)) < -383)
throw new ExtendedIllegalArgumentException("numDecimalPositions (" + String.valueOf((exponent + (bdPrecision - 1))) + ")", ExtendedIllegalArgumentException.RANGE_NOT_VALID);
// compute coefficient digits.
int[] coefficientDigits = new int[16];
int zeroBase = '0';
for (int indx = 0; indx < bdPrecision; indx++)
{
coefficientDigits[(16 - bdPrecision) + indx] = bdUnscaledStr.charAt(indx) - zeroBase;
}
// the result decFloat16 in bits.
long decFloat16Bits = 0L;
// mask the coefficient continuation.
for (int indx = 1; indx < 16; indx += 3)
{
decFloat16Bits <<= 10; // declet. (3 digits are accommondated in 10 bits).
int decDigits = packDenselyPackedDecimal(coefficientDigits, indx);
decFloat16Bits |= decDigits;
}
// mask the exponent continuation.
exponent += DEC_FLOAT_16_BIAS;
decFloat16Bits |= ((exponent & 0xff) << 50);
// mask the combination.
long combination;
if (specialCombination != 0L)
{
combination = specialCombination; //set for Combination G (11111-> NaN, 11110-> (-1)^sign Infinity)
}
else if (coefficientDigits[0] >= 8)
{
combination = 0x18;
combination |= ((exponent & 0x300) >> 7);
combination |= (coefficientDigits[0] & 0x1);
} else
{
combination = 0x0;
combination |= ((exponent & 0x300) >> 5);
combination |= coefficientDigits[0];
}
decFloat16Bits |= (combination << 58);
// mask the sign bit.
if (sign == -1)
{
decFloat16Bits |= DEC_FLOAT_16_SIGN_MASK;
}
as400Value[offset] = (byte) ((decFloat16Bits >> 56) & 0xFF);
as400Value[offset + 1] = (byte) ((decFloat16Bits >> 48) & 0xFF);
as400Value[offset + 2] = (byte) ((decFloat16Bits >> 40) & 0xFF);
as400Value[offset + 3] = (byte) ((decFloat16Bits >> 32) & 0xFF);
as400Value[offset + 4] = (byte) ((decFloat16Bits >> 24) & 0xFF);
as400Value[offset + 5] = (byte) ((decFloat16Bits >> 16) & 0xFF);
as400Value[offset + 6] = (byte) ((decFloat16Bits >> 8) & 0xFF);
as400Value[offset + 7] = (byte) (decFloat16Bits & 0xFF);
if(signalingNaN == 0) //@sig1
as400Value[offset] &= 0xFD; //non signaling (switch off 7th bit) //@sig1
else if (signalingNaN == 1) //@sig1
as400Value[offset] |= 0x02; //signaling (switch on 7th bit) //@sig1
return 8; //always 8 bytes for DECFLOAT16
}
else //DECFLOAT34
{
//get precision of the BigDecimal.
int bdPrecision = SQLDataFactory.getPrecisionForTruncation(inValue, 34)[0]; //bdUnscaledStr.length (); //@rnd1
//bug in jdk1.5 //@max
int zeros = 0; //@max
if((exponent > 6110 ) && bdUnscaledStr.length() < 34){ //maxexp-34 //@max
//pad 0s //@max
zeros = 34 - bdUnscaledStr.length(); //@max
bdUnscaledStr += "00000000000000000000000000000000"; //@max
bdUnscaledStr = bdUnscaledStr.substring(0, 34); //@max
bdPrecision += zeros; //@max
exponent -= zeros; //@max
} //@max
if(bdUnscaledStr.length() > bdPrecision)
exponent = bdUnscaledStr.length() - bdPrecision; //get exponent in terms of precisionForTruncation
// check for error condition.
if ((exponent + (bdPrecision - 1)) > 6144)
throw new ExtendedIllegalArgumentException("numDecimalPositions (" + String.valueOf((exponent + (bdPrecision - 1))) + ")", ExtendedIllegalArgumentException.RANGE_NOT_VALID);
else if ((exponent + (bdPrecision - 1)) < -6143)
throw new ExtendedIllegalArgumentException("numDecimalPositions (" + String.valueOf((exponent + (bdPrecision - 1))) + ")", ExtendedIllegalArgumentException.RANGE_NOT_VALID);
// compute coefficient digits.
int[] coefficientDigits = new int[34];
int zeroBase = '0';
for (int indx = 0; indx < bdPrecision; indx++)
{
coefficientDigits[(34 - bdPrecision) + indx] = bdUnscaledStr.charAt(indx) - zeroBase;
}
// the result decFloat34 in bits.
long decFloat34BitsHi = 0L; // for high 8 bytes.
long decFloat34BitsLo = 0L; // for low 8 bytes.
// mask the coefficient continuation.
int indx = 1;
int decDigits;
// handle the first 12 digits in high 8 bytes.
for (; indx < 13; indx += 3) {
decFloat34BitsHi <<= 10; // 3 digits are accommondated in 10 bits.
decDigits = packDenselyPackedDecimal (coefficientDigits, indx);
decFloat34BitsHi |= decDigits;
}
// handle the 3 digits on the boundary of high and low 8 bytes.
decDigits = packDenselyPackedDecimal (coefficientDigits, indx);
decFloat34BitsHi <<= 6;
decFloat34BitsHi |= ((decDigits & 0x3f0) >> 4); // get high 6 bits of decDigits.
decFloat34BitsLo |= (decDigits & 0xf); // get low 4 bits of decDigits.
indx += 3;
// handle the rest 18 digits in high 8 bytes.
for (; indx < 34; indx += 3)
{
decFloat34BitsLo <<= 10; // 3 digits are accommondated in 10 bits.
decDigits = packDenselyPackedDecimal (coefficientDigits, indx);
decFloat34BitsLo |= decDigits;
}
// mask the exponent continuation.
exponent += DEC_FLOAT_34_BIAS;
decFloat34BitsHi |= ((exponent & 0xfff) << 46);
// mask the combination.
long combination;
if (specialCombination != 0L)
{
combination = specialCombination; //set for Combination G (11111-> NaN, 11110-> (-1)^sign Infinity)
}
else if (coefficientDigits[0] >= 8)
{
combination = 0x18;
combination |= ((exponent & 0x3000) >> 11);
combination |= (coefficientDigits[0] & 0x1);
}
else
{
combination = 0x0;
combination |= ((exponent & 0x3000) >> 9);
combination |= coefficientDigits[0];
}
decFloat34BitsHi |= (combination << 58);
// mask the sign bit.
if (sign == -1)
{
decFloat34BitsHi |= DEC_FLOAT_34_SIGN_MASK;
}
as400Value[offset] = (byte) ((decFloat34BitsHi >> 56) & 0xFF);
as400Value[offset + 1] = (byte) ((decFloat34BitsHi >> 48) & 0xFF);
as400Value[offset + 2] = (byte) ((decFloat34BitsHi >> 40) & 0xFF);
as400Value[offset + 3] = (byte) ((decFloat34BitsHi >> 32) & 0xFF);
as400Value[offset + 4] = (byte) ((decFloat34BitsHi >> 24) & 0xFF);
as400Value[offset + 5] = (byte) ((decFloat34BitsHi >> 16) & 0xFF);
as400Value[offset + 6] = (byte) ((decFloat34BitsHi >> 8) & 0xFF);
as400Value[offset + 7] = (byte) (decFloat34BitsHi & 0xFF);
as400Value[offset + 8] = (byte) ((decFloat34BitsLo >> 56) & 0xFF);
as400Value[offset + 9] = (byte) ((decFloat34BitsLo >> 48) & 0xFF);
as400Value[offset + 10] = (byte) ((decFloat34BitsLo >> 40) & 0xFF);
as400Value[offset + 11] = (byte) ((decFloat34BitsLo >> 32) & 0xFF);
as400Value[offset + 12] = (byte) ((decFloat34BitsLo >> 24) & 0xFF);
as400Value[offset + 13] = (byte) ((decFloat34BitsLo >> 16) & 0xFF);
as400Value[offset + 14] = (byte) ((decFloat34BitsLo >> 8) & 0xFF);
as400Value[offset + 15] = (byte) (decFloat34BitsLo & 0xFF);
if(signalingNaN == 0) //@sig1
as400Value[offset] &= 0xFD; //non signaling (switch off 7th bit) //@sig1
else if (signalingNaN == 1) //@sig1
as400Value[offset] |= 0x02; //signaling (switch on 7th bit) //@sig1
return 16; //always 16 bytes for DECFLOAT34
}
}
/**
* Converts the specified Java object to server format.
*
* @param doubleValue
* The value to be converted to server format. If the decimal part
* of this value needs to be truncated, it will be rounded based on
* decfloat rounding mode property.
* @return The server representation of the data type.
*/
public byte[] toBytes(double doubleValue)
{
byte[] as400Value = new byte[digits == 16 ? 64 : 128];
toBytes(doubleValue, as400Value, 0);
return as400Value;
}
/**
* Converts the specified Java object into server format in
* the specified byte array.
*
* @param doubleValue The value to be converted to server format. If the decimal part
* of this value needs to be truncated, it will be rounded based on
* decfloat rounding mode property.
* @param as400Value The array to receive the data type in server format. There must
* be enough space to hold the server value.
* @return The number of bytes in the server representation of the data type.
**/
public int toBytes(double doubleValue, byte[] as400Value)
{
return toBytes(doubleValue, as400Value, 0);
}
/**
* Converts the specified Java object into server format in the specified byte array.
*
* @param doubleValue The value to be converted to server format. If the decimal part
* of this value needs to be truncated, it will be rounded based on
* decfloat rounding mode property.
* @param as400Value The array to receive the data type in server format.
* There must be enough space to hold the server value.
* @param offset The offset into the byte array for the start of the server value.
* It must be greater than or equal to zero.
* @return The number of bytes in the server representation of the data type.
**/
public int toBytes(double doubleValue, byte[] as400Value, int offset)
{
BigDecimal bd = new BigDecimal(doubleValue);
return toBytes(bd, as400Value, offset);
}
/**
* Converts the specified server data type to a Java double value.
* @param as400Value The array containing the data type in server format.
* The entire data type must be represented.
* @return The Java double value corresponding to the data type.
**/
public double toDouble(byte[] as400Value)
{
return toDouble(as400Value, 0);
}
/**
* Converts the specified server data type to a Java double value.
*
* @param as400Value The array containing the data type in server format.
* The entire data type must be represented.
* @param offset The offset into the byte array for the start of the server value.
* It must be greater than or equal to zero.
* @return The Java double value corresponding to the data type.
**/
public double toDouble(byte[] as400Value, int offset)
{
// Check the offset to prevent bogus NumberFormatException message.
if (offset < 0)
throw new ArrayIndexOutOfBoundsException(String.valueOf(offset));
// Compute the value.
BigDecimal bd = (BigDecimal) this.toObject(as400Value, offset);
return bd.doubleValue();
}
/**
* Converts the specified server data type to a Java object.
* @param as400Value The array containing the data type in server format. The entire data type must be represented.
* @return The BigDecimal object corresponding to the data type.
**/
public Object toObject(byte[] as400Value)
{
return this.toObject(as400Value, 0);
}
/**
* Converts the specified server data type to a Java object (BigDecimal).
* @param as400Value The array containing the data type in server format. The entire data type must be represented and the data type must have valid packed decimal format.
* @param offset The offset into the byte array for the start of the server value. It must be greater than or equal to zero.
* @return The BigDecimal object corresponding to the data type.
**/
public Object toObject(byte[] as400Value, int offset)
{
// Check offset to prevent bogus NumberFormatException message
if (offset < 0)
{
throw new ArrayIndexOutOfBoundsException(String.valueOf(offset));
}
if(this.digits == 16)
{
long decFloat16Bits = BinaryConverter.byteArrayToLong(as400Value, offset);
long combination = (decFloat16Bits & DEC_FLOAT_16_COMBINATION_MASK) >> 58;
//compute sign here so we can get -+Infinity values
int sign = ((decFloat16Bits & DEC_FLOAT_16_SIGN_MASK) == DEC_FLOAT_16_SIGN_MASK) ? -1 : 1;
// deal with special numbers. (not a number and infinity)
if ((combination == 0x1fL) && ( sign == 1))
{
long nanSignal = (decFloat16Bits & DEC_FLOAT_16_SIGNAL_MASK) >> 57; //shift first 7 bits to get signal bit out //@snan
if (nanSignal == 1)
throw new ExtendedIllegalArgumentException("SNaN", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID);
else
throw new ExtendedIllegalArgumentException("NaN", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID);
}
else if ((combination == 0x1fL) && ( sign == -1))
{
long nanSignal = (decFloat16Bits & DEC_FLOAT_16_SIGNAL_MASK) >> 57; //shift first 7 bits to get signal bit out //@snan
if (nanSignal == 1)
throw new ExtendedIllegalArgumentException("-SNaN", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID);
else
throw new ExtendedIllegalArgumentException("-NaN", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID);
}
else if ((combination == 0x1eL) && ( sign == 1))
{
throw new ExtendedIllegalArgumentException("Infinity", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID);
}
else if ((combination == 0x1eL) && ( sign == -1))
{
throw new ExtendedIllegalArgumentException("-Infinity", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID);
}
// compute the exponent MSD and the coefficient MSD.
int exponentMSD;
long coefficientMSD;
if ((combination & 0x18L) == 0x18L) {
// format of 11xxx:
exponentMSD = (int) ((combination & 0x06L) >> 1);
coefficientMSD = 8 + (combination & 0x01L);
}
else {
// format of xxxxx:
exponentMSD = (int) ((combination & 0x18L) >> 3);
coefficientMSD = (combination & 0x07L);
}
// compute the exponent.
int exponent = (int) ((decFloat16Bits & DEC_FLOAT_16_EXPONENT_CONTINUATION_MASK) >> 50);
exponent |= (exponentMSD << 8);
exponent -= DEC_FLOAT_16_BIAS;
// compute the coefficient.
long coefficientContinuation = decFloat16Bits & DEC_FLOAT_16_COEFFICIENT_CONTINUATION_MASK;
int coefficientLo = decFloatBitsToDigits ((int) (coefficientContinuation & 0x3fffffff)); // low 30 bits (9 digits)
int coefficientHi = decFloatBitsToDigits ((int) ((coefficientContinuation >> 30) & 0xfffff)); // high 20 bits (6
// digits)
coefficientHi += coefficientMSD * 1000000L;
// compute the int array of coefficient.
int[] value = computeMagnitude (new int[] { coefficientHi, coefficientLo });
// convert value to a byte array of coefficient.
byte[] magnitude = new byte[8];
magnitude[0] = (byte) (value[0] >>> 24);
magnitude[1] = (byte) (value[0] >>> 16);
magnitude[2] = (byte) (value[0] >>> 8);
magnitude[3] = (byte) (value[0]);
magnitude[4] = (byte) (value[1] >>> 24);
magnitude[5] = (byte) (value[1] >>> 16);
magnitude[6] = (byte) (value[1] >>> 8);
magnitude[7] = (byte) (value[1]);
BigInteger bigInt = new java.math.BigInteger (sign, magnitude);
return getNewBigDecimal(bigInt, -exponent);
}else
{
//decfloat34
long decFloat34BitsHi = BinaryConverter.byteArrayToLong (as400Value, offset);
long decFloat34BitsLo = BinaryConverter.byteArrayToLong (as400Value, offset + 8);
long combination = (decFloat34BitsHi & DEC_FLOAT_34_COMBINATION_MASK) >> 58;
//compute sign.
int sign = ((decFloat34BitsHi & DEC_FLOAT_34_SIGN_MASK) == DEC_FLOAT_34_SIGN_MASK) ? -1 : 1;
// deal with special numbers.
if ((combination == 0x1fL) && ( sign == 1))
{
long nanSignal = (decFloat34BitsHi & DEC_FLOAT_34_SIGNAL_MASK) >> 57; //shift first 7 bits to get signal bit out //@snan
if (nanSignal == 1)
throw new ExtendedIllegalArgumentException("SNaN", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID);
else
throw new ExtendedIllegalArgumentException("NaN", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID);
}
else if ((combination == 0x1fL) && ( sign == -1))
{
long nanSignal = (decFloat34BitsHi & DEC_FLOAT_34_SIGNAL_MASK) >> 57; //shift first 7 bits to get signal bit out //@snan
if (nanSignal == 1)
throw new ExtendedIllegalArgumentException("-SNaN", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID);
else
throw new ExtendedIllegalArgumentException("-NaN", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID);
}
else if ((combination == 0x1eL) && ( sign == 1))
{
throw new ExtendedIllegalArgumentException("Infinity", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID);
}
else if ((combination == 0x1eL) && ( sign == -1))
{
throw new ExtendedIllegalArgumentException("-Infinity", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID);
}
// compute the exponent MSD and the coefficient MSD.
int exponentMSD;
long coefficientMSD;
if ((combination & 0x18L) == 0x18L) {
// format of 11xxx:
exponentMSD = (int) ((combination & 0x06L) >> 1);
coefficientMSD = 8 + (combination & 0x01L);
}
else {
// format of xxxxx:
exponentMSD = (int) ((combination & 0x18L) >> 3);
coefficientMSD = (combination & 0x07L);
}
// compute the exponent.
int exponent = (int) ((decFloat34BitsHi & DEC_FLOAT_34_EXPONENT_CONTINUATION_MASK) >> 46);
exponent |= (exponentMSD << 12);
exponent -= DEC_FLOAT_34_BIAS;
// compute the coefficient.
int coefficientLo = decFloatBitsToDigits ((int) (decFloat34BitsLo & 0x3fffffff)); // last 30 bits (9 digits)
// another 30 bits (9 digits)
int coefficientMeLo = decFloatBitsToDigits ((int) ((decFloat34BitsLo >> 30) & 0x3fffffff));
// another 30 bits (9 digits). 26 bits from hi and 4 bits from lo.
int coefficientMeHi = decFloatBitsToDigits ((int) (((decFloat34BitsHi & 0x3ffffff) << 4) | ((decFloat34BitsLo >> 60) & 0xf)));
int coefficientHi = decFloatBitsToDigits ((int) ((decFloat34BitsHi >> 26) & 0xfffff)); // high 20 bits (6 digits)
coefficientHi += coefficientMSD * 1000000L;
// compute the int array of coefficient.
int[] value = computeMagnitude (new int[] { coefficientHi, coefficientMeHi, coefficientMeLo, coefficientLo });
// convert value to a byte array of coefficient.
byte[] magnitude = new byte[16];
magnitude[0] = (byte) (value[0] >>> 24);
magnitude[1] = (byte) (value[0] >>> 16);
magnitude[2] = (byte) (value[0] >>> 8);
magnitude[3] = (byte) (value[0]);
magnitude[4] = (byte) (value[1] >>> 24);
magnitude[5] = (byte) (value[1] >>> 16);
magnitude[6] = (byte) (value[1] >>> 8);
magnitude[7] = (byte) (value[1]);
magnitude[8] = (byte) (value[2] >>> 24);
magnitude[9] = (byte) (value[2] >>> 16);
magnitude[10] = (byte) (value[2] >>> 8);
magnitude[11] = (byte) (value[2]);
magnitude[12] = (byte) (value[3] >>> 24);
magnitude[13] = (byte) (value[3] >>> 16);
magnitude[14] = (byte) (value[3] >>> 8);
magnitude[15] = (byte) (value[3]);
java.math.BigInteger bigInt = new java.math.BigInteger (sign, magnitude);
return getNewBigDecimal(bigInt, -exponent);
}
}
// /**
// * helper method to throw exception during conversion
// */
// static final void throwNumberFormatException(boolean highNibble, int byteOffset, int byteValue, byte[] fieldBytes) throws NumberFormatException
// {
// AS400PackedDecimal.throwNumberFormatException(highNibble, byteOffset, byteValue, fieldBytes);
// }
/**
* Converts byte to string */
static final String byteToString(int byteVal)
{
int leftDigitValue = (byteVal >>> 4) & 0x0F;
int rightDigitValue = byteVal & 0x0F;
char[] digitChars = new char[2];
// 0x30 = '0', 0x41 = 'A'
digitChars[0] = leftDigitValue < 0x0A ? (char)(0x30 + leftDigitValue) : (char)(leftDigitValue - 0x0A + 0x41);
digitChars[1] = rightDigitValue < 0x0A ? (char)(0x30 + rightDigitValue) : (char)(rightDigitValue - 0x0A + 0x41);
return new String(digitChars);
}
/**
* Internal declet encoding helper method.
*
**/
private static int packDenselyPackedDecimal (int[] digits, int indx)
{
//Declet is the three bit encoding of one decimal digit. The Decfloat is made up of declets to represent
//the decfloat 16 or 34 digits
int result = 0;
int combination = ((digits[indx+0] & 8) >> 1) | ((digits[indx+1] & 8) >> 2) | ((digits[indx+2] & 8) >> 3);
switch (combination) {
case 0: // no, no, no
result = (digits[indx+0] << 7) | (digits[indx+1] << 4) | digits[indx+2];
break;
case 1: // no, no, yes
result = (digits[indx+0] << 7) | (digits[indx+1] << 4) | (digits[indx+2] & 1) | 8;
break;
case 2: // no, yes, no
result = (digits[indx+0] << 7) | ((digits[indx+2] & 6) << 4) | ((digits[indx+1] & 1) << 4) | (digits[indx+2] & 1) | 10;
break;
case 3: // no, yes, yes
result = (digits[indx+0] << 7) | ((digits[indx+1] & 1) << 4) | (digits[indx+2] & 1) | 78;
break;
case 4: // yes, no, no
result = ((digits[indx+2] & 6) << 7) | ((digits[indx+0] & 1) << 7) | (digits[indx+1] << 4) | (digits[indx+2] & 1) | 12;
break;
case 5: // yes, no, yes
result = ((digits[indx+1] & 6) << 7) | ((digits[indx+0] & 1) << 7) | ((digits[indx+1] & 1) << 4) | (digits[indx+2] & 1) | 46;
break;
case 6: // yes, yes, no
result = ((digits[indx+2] & 6) << 7) | ((digits[indx+0] & 1) << 7) | ((digits[indx+1] & 1) << 4) | (digits[indx+2] & 1) | 14;
break;
case 7: // yes, yes, yes
result = ((digits[indx+0] & 1) << 7) | ((digits[indx+1] & 1) << 4) | (digits[indx+2] & 1) | 110;
break;
}
return result;
}
/**
* Internal declet decoding helper method.
**/
private static int unpackDenselyPackedDecimal (int bits)
{
//Declet is the three bit encoding of one decimal digit. The Decfloat is made up of declets to represent
//the decfloat 16 or 34 digits
int combination;
if ((bits & 14) == 14) combination = ((bits & 96) >> 5) | 4;
else combination = ((bits & 8) == 8) ? (((~bits) & 6) >> 1) : 0;
int decoded = 0;
switch (combination)
{
case 0: // bit 6 is 0
decoded = ((bits & 896) << 1) | (bits & 119);
break;
case 1: // bits 6,7,8 are 1-1-0
decoded = ((bits & 128) << 1) | (bits & 113) | ((bits & 768) >> 7) | 2048;
break;
case 2: // bits 6,7,8 are 1-0-1
decoded = ((bits & 896) << 1) | (bits & 17) | ((bits & 96) >> 4) | 128;
break;
case 3: // bits 6,7,8 are 1-0-0
decoded = ((bits & 896) << 1) | (bits & 113) | 8;
break;
case 4: // bits 6,7,8 are 1-1-1, bits 3,4 are 0-0
decoded = ((bits & 128) << 1) | (bits & 17) | ((bits & 768) >> 7) | 2176;
break;
case 5: // bits 6,7,8 are 1-1-1, bits 3,4 are 0-1
decoded = ((bits & 128) << 1) | (bits & 17) | ((bits & 768) >> 3) | 2056;
break;
case 6: // bits 6,7,8 are 1-1-1, bits 3,4 are 1-0
decoded = ((bits & 896) << 1) | (bits & 17) | 136;
break;
case 7: // bits 6,7,8 are 1-1-1, bits 3,4 are 1-1
// NB: we ignore values of bits 0,1 in this case
decoded = ((bits & 128) << 1) | (bits & 17) | 2184;
break;
}
return ((decoded & 3840) >> 8) * 100 + ((decoded & 240) >> 4) *10 + (decoded & 15);
}
/**
* Compute the int array of magnitude from input value segments.
*/
private static final int[] computeMagnitude (int[] input)
{
int length = input.length;
int[] mag = new int[length];
mag[length - 1] = input[length - 1];
for (int i = 0; i < length - 1; i++) {
int carry = 0;
int j = tenRadixMagnitude[i].length - 1;
int k = length - 1;
for (; j >= 0; j--, k--) {
long product = (input[length - 2 - i] & 0xFFFFFFFFL) * (tenRadixMagnitude[i][j] & 0xFFFFFFFFL)
+ (mag[k] & 0xFFFFFFFFL) // add previous value
+ (carry & 0xFFFFFFFFL); // add carry
carry = (int) (product >>> 32);
mag[k] = (int) (product & 0xFFFFFFFFL);
}
mag[k] = (int) carry;
}
return mag;
}
/**
* Convert 30 binary bits coefficient to 9 decimal digits. Note that for performance purpose,
* it does not do array-out-of-bound checking.
*/
private static final int decFloatBitsToDigits (int bits)
{
int decimal = 0;
for (int i = 2; i >= 0; i--) {
decimal *= 1000;
decimal += unpackDenselyPackedDecimal ((int)((bits >> (i * 10)) & 0x03ffL));
}
return decimal;
}
/**
* This method rounds the number (unscaled integer value and exponent).
* mcPrecision and mcRoundingMode are what is in jdk 5.0 MathContext.
* What is returned from this method should be the same as what jre 5.0 would
* have rounded to using MathContext and BigDecimal.
*/
private static BigDecimal roundByModePreJDK5(BigInteger intVal, int scale, int mcPrecision, String mcRoundingMode)
{
BigInteger roundingMax = null;
if (mcPrecision == 16)
roundingMax = new BigInteger("10000000000000000"); // 16 0s
else
roundingMax = new BigInteger("10000000000000000000000000000000000"); // 34 0s
BigInteger roundingMin = roundingMax.negate();
if (roundingMax != null && intVal.compareTo(roundingMax) < 0
&& intVal.compareTo(roundingMin) > 0)
return getNewBigDecimal(intVal, scale); //rounding not needed
//get precision from intVal without 0's on right side
int[] values = SQLDataFactory.getPrecisionForTruncation(getNewBigDecimal(intVal, scale), mcPrecision); //=precisionStr.length() - trimCount; //@rnd1
int precisionNormalized = values[0]; //@rnd1
int droppedZeros = values[1]; //@rnd1 decrease scale by number of zeros removed from precision //@rnd1
if(droppedZeros != 0) //@rnd1
{ //@rnd1
//adjust intVal number of zeros removed off end //@rnd1
intVal = intVal.divide( new BigInteger("10").pow(droppedZeros)); //@rnd1
} //@rnd1
//get number of digits to round off
int drop = precisionNormalized - mcPrecision;
//@rnd1 if (drop <= 0)
//@rnd1 return getNewBigDecimal(intVal, scale);
BigDecimal rounded = roundOffDigits(intVal, scale, mcRoundingMode, drop);
if(droppedZeros != 0) //@rnd1
{ //@rnd1
//adjust rounded bigdecimal by dropped zero count //@rnd1
rounded = rounded.movePointRight(droppedZeros); //@rnd1
} //@rnd1
return rounded; //@rnd1
}
/**
* Helper method to round off digits
*/
private static BigDecimal roundOffDigits(BigInteger intVal, int scale,
String mcRoundingMode, int dropCount)
{
BigDecimal divisor = new BigDecimal((new BigInteger("10")).pow(dropCount), 0);
BigDecimal preRoundedBD = getNewBigDecimal(intVal, scale);
int roundingMode = 0;
try
{
//get int value for RoundingMode from BigDecimal
roundingMode = ((Integer) Class.forName("java.math.BigDecimal").getDeclaredField(mcRoundingMode).get(null)).intValue();
} catch (Exception e)
{
throw new InternalErrorException(InternalErrorException.UNKNOWN, e); //should never happen
}
BigDecimal rounded = preRoundedBD.divide(divisor, scale, roundingMode); // do actual rounding here
BigInteger bigIntPart = rounded.unscaledValue();
rounded = getNewBigDecimal(bigIntPart, scale - dropCount);
return rounded;
}
/**
Creates and returns a new BigDecimal based on parameters.
This is a temporary hack due to pre-jre 1.5 not being able to handle negative scales (positive exp)
After we no longer support pre-java 1.5, this method can be replaced with new BigDecimal(bigInt, scale).
@param bigInt BigInteger part.
@param scale scale part.
**/
private static BigDecimal getNewBigDecimal(BigInteger bigInt, int scale)
{
BigDecimal bigDecimal = null;
try{
bigDecimal = new BigDecimal(bigInt, scale);
}catch(NumberFormatException e)
{
//note that creating BigDecimal with negative scale is ok in 5, but not in 1.4
//deal with negative scale in pre jdk 5.0 here
if (scale > 0)
throw e;
bigDecimal = new BigDecimal(bigInt);
bigDecimal = bigDecimal.movePointRight(-scale);
}
return bigDecimal;
}
//Decimal float. //@DFA
/**
Rounds the precision of a BigDecimal by removing least significant digits from
the right side of the precision. (least significant digits could be left or right of implicit decimal point)
@param bd BigDecimal to truncate.
@param precision to truncate bd to. (16 or 34)
@param roundingMode to use when truncating
* @return the rounded BigDecimal
**/
public static BigDecimal roundByMode(BigDecimal bd, int precision, String roundingMode)
{
BigDecimal roundedBD = null;
//MathContext is in jdk1.5. So use reflection so code will build under pre-1.5
//later, use this when we move to jdk1.5
//All we are doing below is: bdAbs = inValue.abs(new MathContext(16, roundingMode));
boolean isGEJVM50 = true;
try
{
//in this try block, we do rounding via BigDecimal and MathContext
Class cls = Class.forName("java.math.MathContext"); //thorw ClassNotFoundException if pre 1.5 jvm
Constructor ct = cls.getConstructor(new Class[] { Integer.TYPE, Class.forName("java.math.RoundingMode") });
Object arglist[] = new Object[2];
arglist[0] = Integer.valueOf(precision); //MathContext.DECIMAL64 (16 or 34 char decfloat precision)
arglist[1] = Class.forName("java.math.RoundingMode").getDeclaredField(roundingMode.substring(6)).get(null); //@pdc remove "ROUND_"
Object mathContextRounded = ct.newInstance(arglist); //ie. new MathContext(16or34, RoundingMode.x);
Object[] arglist2 = new Object[]{mathContextRounded};
Class[] c = new Class[] { Class.forName("java.math.MathContext") };
java.lang.reflect.Method method = java.math.BigDecimal.class.getDeclaredMethod("round", c);
roundedBD = (java.math.BigDecimal) method.invoke(bd, arglist2);
}
//Unfortunately, we cannot just catch Exception since we do not want to miss any real exceptions
//from rounding etc. And can't re-throw Exception it since method is not declared with "throws"
catch (ClassNotFoundException e)
{
//got exception due to pre-java 5.0.
isGEJVM50 = false;
}
catch (NoSuchMethodException e)
{ isGEJVM50 = false; }
catch (NoSuchFieldException e)
{ isGEJVM50 = false; }
catch (IllegalAccessException e)
{ isGEJVM50 = false; }
catch (InvocationTargetException e)
{ isGEJVM50 = false; }
catch (InstantiationException e)
{ isGEJVM50 = false; }
if(isGEJVM50 == false)
{
//use our rounding code to round in pre java 5.0
roundedBD = roundByModePreJDK5(bd.unscaledValue(), bd.scale(), precision, roundingMode);
}
return roundedBD;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy