src.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-jdk8 Show documentation
Show all versions of jt400-jdk8 Show documentation
The Open Source version of the IBM Toolbox for Java
///////////////////////////////////////////////////////////////////////////////
//
// 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);
}
}
/**
* 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); //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] = new Integer(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;
}
}