/******************************************************************************* * Copyright © 2006, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * *******************************************************************************/ package org.eclipse.edt.javart.util; import java.math.BigInteger; import org.eclipse.edt.javart.messages.Message; import org.eclipse.edt.javart.resources.Platform; import eglx.lang.AnyException; import eglx.lang.InvalidArgumentException; /** * Utility methods that are used by the various "Numeric" Value classes. * * @author mheitz */ public class NumericUtil { /** * This is BigInteger.valueOf( 100000000000000000L ). */ private final static BigInteger TEN_TO_THE_SEVENTEENTH = BigInteger.valueOf( 100000000000000000L ); /** * The sign mask for positive NUM and NUMC values in ASCII. */ public final static byte ASCII_POSITIVE_NUM_NUMC_MASK = 0x30; /** * The sign mask for positive NUM values in EBCDIC. */ public final static byte EBCDIC_POSITIVE_NUM_MASK = (byte)0xF0; /** * The sign mask for positive NUM values in the local character encoding. */ public final static byte LOCAL_POSITIVE_NUM_MASK; /** * The sign mask for positive NUMC values in EBCDIC. */ public final static byte EBCDIC_POSITIVE_NUMC_MASK = (byte)0xC0; /** * The sign mask for positive NUMC values in the local character encoding. */ public final static byte LOCAL_POSITIVE_NUMC_MASK; /** * The sign mask for negative NUM and NUMC values in ASCII. */ public final static byte ASCII_NEGATIVE_NUM_NUMC_MASK = 0x70; /** * The sign mask for negative NUM and NUMC values in EBCDIC. */ public final static byte EBCDIC_NEGATIVE_NUM_NUMC_MASK = (byte)0xD0; /** * The sign mask for negative NUM and NUMC values in the local character * encoding. */ public final static byte LOCAL_NEGATIVE_NUM_NUMC_MASK; /** * The difference between the number zero and the character zero in ASCII. */ public final static byte ASCII_ZERO_DIFF = 0x30; /** * The difference between the number zero and the character zero in EBCDIC. */ public final static byte EBCDIC_ZERO_DIFF = (byte)0xF0; /** * The difference between the number zero and the character zero in the local * character encoding. */ public final static byte LOCAL_ZERO_DIFF; // Initialize the "LOCAL" constants. static { if ( Platform.IS_ASCII ) { LOCAL_POSITIVE_NUM_MASK = ASCII_POSITIVE_NUM_NUMC_MASK; LOCAL_POSITIVE_NUMC_MASK = ASCII_POSITIVE_NUM_NUMC_MASK; LOCAL_NEGATIVE_NUM_NUMC_MASK = ASCII_NEGATIVE_NUM_NUMC_MASK; LOCAL_ZERO_DIFF = ASCII_ZERO_DIFF; } else { LOCAL_POSITIVE_NUM_MASK = EBCDIC_POSITIVE_NUM_MASK; LOCAL_POSITIVE_NUMC_MASK = EBCDIC_POSITIVE_NUMC_MASK; LOCAL_NEGATIVE_NUM_NUMC_MASK = EBCDIC_NEGATIVE_NUM_NUMC_MASK; LOCAL_ZERO_DIFF = EBCDIC_ZERO_DIFF; } } /** * Returns the length in bytes of an item with the given characteristics. * * @param digits how many digits it has. * @param isNum true for NUM and NUMC, false for DECIMAL, MONEY, or PACF. * @return the length in bytes of the item. */ public static int getLengthInBytes( int digits, boolean isNum ) { if ( isNum ) { // There's one byte per digit. return digits; } else { // This works for DECIMAL, MONEY, and PACF. There are two digits // per byte, plus half a byte for the sign. Round up to the nearest // whole byte. return (digits / 2) + 1; } } /** * Converts a value to DECIMAL or PACF format, writing it into the buffer at * the specified position. * * @param value the value. * @param buffer where to write the value. * @param offset index of the first byte in the buffer. * @param length the number of digits to write. * @param itemLength length of the value in bytes. * @param pos the positive sign to use: 0xC for DECIMAL, 0xF for PACF. */ public static void toDecimal( int value, byte[] buffer, int offset, int length, int itemLength, byte pos ) { int bufferIndex = offset + itemLength - 1; // Make sure the value is positive and set the sign. if ( value >= 0 ) { buffer[ bufferIndex ] = pos; } else { value = -value; buffer[ bufferIndex ] = (byte)0x0D; } // Write the digits, starting from the end and working backwards. boolean wroteLow = true; int digitsToWrite = length; while ( value > 0 && digitsToWrite > 0 ) { // Set the high nibble of the current byte. buffer[ bufferIndex ] |= (byte)((value % 10) << 4); digitsToWrite--; wroteLow = false; value = value / 10; // We're done with this byte. bufferIndex--; if ( value == 0 || digitsToWrite == 0 ) { // We're done with value. break; } // Set the low nibble of the current byte. buffer[ bufferIndex ] = (byte)(value % 10); digitsToWrite--; wroteLow = true; value = value / 10; } // Fill with zeros if neccessary. if ( digitsToWrite > 0 ) { if ( wroteLow ) { // Since we just wrote the low nibble of a byte, the high nibble // is already a zero. We can move to the next byte. digitsToWrite--; bufferIndex--; } while ( digitsToWrite > 0 ) { // Zero out two nibbles at a time. buffer[ bufferIndex ] = 0; bufferIndex--; digitsToWrite -= 2; } } } /** * Converts a value to DECIMAL or PACF format, writing it into the buffer at * the specified position. * * @param value the value. * @param buffer where to write the value. * @param offset index of the first byte in the buffer. * @param length the number of digits to write. * @param itemLength length of the value in bytes. * @param pos the positive sign to use: 0xC for DECIMAL, 0xF for PACF. */ public static void toDecimal( long value, byte[] buffer, int offset, int length, int itemLength, byte pos ) { int bufferIndex = offset + itemLength - 1; // Make sure the value is positive and set the sign. if ( value >= 0 ) { buffer[ bufferIndex ] = pos; } else { value = -value; buffer[ bufferIndex ] = (byte)0x0D; } // Write the digits, starting from the end and working backwards. boolean wroteLow = true; int digitsToWrite = length; while ( value > 0 && digitsToWrite > 0 ) { // Set the high nibble of the current byte. buffer[ bufferIndex ] |= (byte)((value % 10) << 4); digitsToWrite--; wroteLow = false; value = value / 10; // We're done with this byte. bufferIndex--; if ( value == 0 || digitsToWrite == 0 ) { // We're done with value. break; } // Set the low nibble of the current byte. buffer[ bufferIndex ] = (byte)(value % 10); digitsToWrite--; wroteLow = true; value = value / 10; } // Fill with zeros if neccessary. if ( digitsToWrite > 0 ) { if ( wroteLow ) { // Since we just wrote the low nibble of a byte, the high nibble // is already a zero. We can move to the next byte. digitsToWrite--; bufferIndex--; } while ( digitsToWrite > 0 ) { // Zero out two nibbles at a time. buffer[ bufferIndex ] = 0; bufferIndex--; digitsToWrite -= 2; } } } /** * Converts a value to DECIMAL or PACF format, writing it into the buffer at * the specified position. Do not use this method if the data has fewer than * 18 digits. In that case, do this instead: * toDecimal( value.longValue(), ... ) ). * * @param value the value. * @param buffer where to write the value. * @param offset index of the first byte in the buffer. * @param length the number of digits to write, must be at least 18. * @param itemLength length of the value in bytes. * @param pos the positive sign to use: 0xC for DECIMAL, 0xF for PACF. */ public static void toDecimal( BigInteger value, byte[] buffer, int offset, int length, int itemLength, byte pos ) { if ( value.bitLength() < 63 ) { // The value can be stored in a long, so use the method that doesn't // operate on BigIntegers. It's much faster than this one. toDecimal( value.longValue(), buffer, offset, length, itemLength, pos ); return; } // Split the value into two BigIntegers that can be converted to longs. BigInteger[] topAndBottom = value.divideAndRemainder( TEN_TO_THE_SEVENTEENTH ); // Write the bottom half and sign using the other toDecimal method. int byteOfSeventeethDigit = offset + (length / 2) - 8; long bottom = topAndBottom[ 1 ].longValue(); toDecimal( bottom, buffer, byteOfSeventeethDigit, 17, 9, pos ); // Convert the top half to a long, and make sure it's positive since the // sign has already been written. long top = topAndBottom[ 0 ].longValue(); if ( top < 0 ) { top = -top; } // Write the top half of the digits. Start from the end (digit 18) and // work backwards. int digitsToWrite = length - 17; int bufferIndex = byteOfSeventeethDigit - 1; while ( top > 0 && digitsToWrite > 0 ) { // Set the low nibble of the current byte. buffer[ bufferIndex ] = (byte)(top % 10); digitsToWrite--; top = top / 10; if ( top == 0 || digitsToWrite == 0 ) { // We're done with top. If any zeros need to be added, we can // skip the high nibble of this byte. It's already a zero. bufferIndex--; digitsToWrite--; break; } // Set the high nibble of the current byte. buffer[ bufferIndex ] |= (byte)((top % 10) << 4 ); digitsToWrite--; top = top / 10; // We're done with this byte. bufferIndex--; } // Fill with zeros as necessary. while ( digitsToWrite > 0 ) { // Zero out two nibbles at a time. buffer[ bufferIndex ] = 0; bufferIndex--; digitsToWrite -= 2; } } /** * Returns a long made from the DECIMAL or PACF data in the buffer. * * @param buffer where to read the value from. * @param offset index of the first byte in the buffer. * @param length the number of digits to read. * @return the value as a long. * @throws InvalidArgumentException if the bytes are not in the expected format. */ public static long decimalToLong( byte[] buffer, int offset, int length ) throws AnyException { // Make a long from the first digit, which is in the high nibble // of the first byte. long result = (buffer[ offset ] & 0xF0) >> 4; // Now loop over the rest of the digits. for ( int lastByte = offset + (length / 2); offset < lastByte; ) { // Pick up the next digit, which is in the low nibble of the byte. int nextDigit = buffer[ offset ] & 0xF; if ( nextDigit < 10 ) { result = result * 10 + nextDigit; } else { throw new InvalidArgumentException().fillInMessage( Message.INVALID_DATA, "decimal" ); } // Move to the next byte. offset++; // Pick up the next digit, which is in the high nibble of the byte. nextDigit = (buffer[ offset ] & 0xF0) >> 4; if ( nextDigit < 10 ) { result = result * 10 + nextDigit; } else { throw new InvalidArgumentException().fillInMessage( Message.INVALID_DATA, "decimal" ); } } // Now check the sign, which is in the low nibble of the byte. The // negative sign is usually 0xD, but we recognize 0xB as well. int sign = buffer[ offset ] & 0xF; if ( sign == 0xC || sign == 0xF ) { return result; } else if ( sign == 0xD || sign == 0xB ) { return -result; } else { throw new InvalidArgumentException().fillInMessage( Message.INVALID_DATA, "decimal" ); } } /** * Returns a BigInteger made from the DECIMAL or PACF data in the buffer. * * @param buffer where to read the value from. * @param offset index of the first byte in the buffer. * @param length the number of digits to read. * @return the value as a BigInteger. * @throws InvalidArgumentException if the bytes are not in the expected format. */ public static BigInteger decimalToBigInteger( byte[] buffer, int offset, int length ) throws AnyException { // Avoid using BigIntegers as much as possible. if ( length <= 17 ) { return BigInteger.valueOf( decimalToLong( buffer, offset, length ) ); } // Get the bottom 17 digits and the sign as a long. int byteOfSeventeethDigit = offset + (length / 2) - 8; long bottom = decimalToLong( buffer, byteOfSeventeethDigit, 17 ); // Note the sign for later and make the value positive. boolean positive = true; if ( bottom < 0 ) { bottom = -bottom; positive = false; } // Make a long from the top 1-15 digits. Start by grabbing the digits // from the first byte. long top = (buffer[ offset ] & 0xF0) >> 4; if ( top > 9 ) { throw new InvalidArgumentException().fillInMessage( Message.INVALID_DATA, "decimal" ); } int nextDigit = buffer[ offset ] & 0xF; if ( nextDigit < 10 ) { top = top * 10 + nextDigit; } else { throw new InvalidArgumentException().fillInMessage( Message.INVALID_DATA, "decimal" ); } // Collect the rest of the remaining digits. for ( offset++; offset < byteOfSeventeethDigit; offset++ ) { // Add in the digit from the high nibble. nextDigit = (buffer[ offset ] & 0xF0) >> 4; if ( nextDigit < 10 ) { top = top * 10 + nextDigit; } else { throw new InvalidArgumentException().fillInMessage( Message.INVALID_DATA, "decimal" ); } // Add in the digit from the low nibble. nextDigit = buffer[ offset ] & 0xF; if ( nextDigit < 10 ) { top = top * 10 + nextDigit; } else { throw new InvalidArgumentException().fillInMessage( Message.INVALID_DATA, "decimal" ); } } // Now merge bottom and top into a BigInteger. BigInteger bi; if ( top == 0 ) { // There aren't any significant digits in top. bi = BigInteger.valueOf( bottom ); } else { // The value is (top * 10^17) + bottom. bi = BigInteger.valueOf( top ); bi = bi.multiply( TEN_TO_THE_SEVENTEENTH ); bi = bi.add( BigInteger.valueOf( bottom ) ); } // Correct the sign if we changed it. if ( positive ) { return bi; } else { return bi.negate(); } } /** * Writes the value zero in NUM, NUMC, DECIMAL, or PACF format into the buffer * at the specified position. * * @param buffer where to write the value. * @param offset index of the first byte in the buffer. * @param itemLength length of the value in bytes. * @param pos the positive sign to use. * @param zero the byte that indicates zero. */ public static void toZero( byte[] buffer, int offset, int itemLength, byte pos, byte zero ) { // Fill with zeros. int i = 0; for ( ; i < itemLength - 1; i++ ) { buffer[ offset + i ] = zero; } // Set the sign. buffer[ offset + i ] = pos; } /** * Converts a value to NUM or NUMC format, writing it into the buffer at * the specified position. * * @param value the value. * @param buffer where to write the value. * @param offset index of the first byte in the buffer. * @param length the number of digits to write. * @param ascii true if it's ASCII format. * @param num true if it's NUM not NUMC. */ public static void toNum( int value, byte[] buffer, int offset, int length, boolean ascii, boolean num ) { if ( ascii ) { toNum( value, buffer, offset, length, ASCII_POSITIVE_NUM_NUMC_MASK, ASCII_NEGATIVE_NUM_NUMC_MASK, ASCII_ZERO_DIFF ); } else { toNum( value, buffer, offset, length, num ? EBCDIC_POSITIVE_NUM_MASK : EBCDIC_POSITIVE_NUMC_MASK, EBCDIC_NEGATIVE_NUM_NUMC_MASK, EBCDIC_ZERO_DIFF ); } } /** * Converts a value to NUM or NUMC format, writing it into the buffer at * the specified position. * * @param value the value. * @param buffer where to write the value. * @param offset index of the first byte in the buffer. * @param length the number of digits to write. * @param pos the positive sign mask to use. * @param neg the negative sign mask to use. * @param zero the difference between the number zero and the character * zero in the character encoding of the buffer. */ public static void toNum( int value, byte[] buffer, int offset, int length, byte pos, byte neg, byte zero ) { int bufferIndex = offset + length - 1; // Make sure the value is positive, then set the sign and last digit. if ( value >= 0 ) { buffer[ bufferIndex ] = (byte)(pos | (value % 10)); } else { value = -value; buffer[ bufferIndex ] = (byte)(neg | (value % 10)); } // Write the rest of the digits, starting from the end and working backwards. int digitsToWrite = length - 1; for ( value = value / 10; value > 0 && digitsToWrite > 0; digitsToWrite-- ) { bufferIndex--; buffer[ bufferIndex ] = (byte)((value % 10) + zero); value = value / 10; } // Fill with zeros if neccessary. while ( digitsToWrite > 0 ) { digitsToWrite--; bufferIndex--; buffer[ bufferIndex ] = zero; } } /** * Converts a value to NUM or NUMC format, writing it into the buffer at * the specified position. * * @param value the value. * @param buffer where to write the value. * @param offset index of the first byte in the buffer. * @param length the number of digits to write. * @param ascii true if it's ASCII format. * @param num true if it's NUM not NUMC. */ public static void toNum( long value, byte[] buffer, int offset, int length, boolean ascii, boolean num ) { if ( ascii ) { toNum( value, buffer, offset, length, ASCII_POSITIVE_NUM_NUMC_MASK, ASCII_NEGATIVE_NUM_NUMC_MASK, ASCII_ZERO_DIFF ); } else { toNum( value, buffer, offset, length, num ? EBCDIC_POSITIVE_NUM_MASK : EBCDIC_POSITIVE_NUMC_MASK, EBCDIC_NEGATIVE_NUM_NUMC_MASK, EBCDIC_ZERO_DIFF ); } } /** * Converts a value to NUM or NUMC format, writing it into the buffer at * the specified position. * * @param value the value. * @param buffer where to write the value. * @param offset index of the first byte in the buffer. * @param length the number of digits to write. * @param pos the positive sign mask to use. * @param neg the negative sign mask to use. * @param zero the difference between the number zero and the character * zero in the character encoding of the buffer. */ public static void toNum( long value, byte[] buffer, int offset, int length, byte pos, byte neg, byte zero ) { int bufferIndex = offset + length - 1; // Make sure the value is positive, then set the sign and last digit. if ( value >= 0 ) { buffer[ bufferIndex ] = (byte)(pos | (value % 10)); } else { value = -value; buffer[ bufferIndex ] = (byte)(neg | (value % 10)); } // Write the rest of the digits, starting from the end and working backwards. int digitsToWrite = length - 1; for ( value = value / 10; value > 0 && digitsToWrite > 0; digitsToWrite-- ) { bufferIndex--; buffer[ bufferIndex ] = (byte)((value % 10) + zero); value = value / 10; } // Fill with zeros if neccessary. while ( digitsToWrite > 0 ) { digitsToWrite--; bufferIndex--; buffer[ bufferIndex ] = zero; } } /** * Converts a value to NUM or NUMC format, writing it into the buffer at * the specified position. Do not use this method if the data has fewer than * 18 digits. In that case, do this instead: toNum( value.longValue(), ... ) ). * * @param value the value. * @param buffer where to write the value. * @param offset index of the first byte in the buffer. * @param length the number of digits to write. * @param ascii true if it's ASCII format. * @param num true if it's NUM not NUMC. */ public static void toNum( BigInteger value, byte[] buffer, int offset, int length, boolean ascii, boolean num ) { if ( ascii ) { toNum( value, buffer, offset, length, ASCII_POSITIVE_NUM_NUMC_MASK, ASCII_NEGATIVE_NUM_NUMC_MASK, ASCII_ZERO_DIFF ); } else { toNum( value, buffer, offset, length, num ? EBCDIC_POSITIVE_NUM_MASK : EBCDIC_POSITIVE_NUMC_MASK, EBCDIC_NEGATIVE_NUM_NUMC_MASK, EBCDIC_ZERO_DIFF ); } } /** * Converts a value to NUM or NUMC format, writing it into the buffer at * the specified position. Do not use this method if the data has fewer than * 18 digits. In that case, do this instead: toNum( value.longValue(), ... ) ). * * @param value the value. * @param buffer where to write the value. * @param offset index of the first byte in the buffer. * @param length the number of digits to write. * @param pos the positive sign mask to use. * @param neg the negative sign mask to use. * @param zero the difference between the number zero and the character * zero in the character encoding of the buffer. */ public static void toNum( BigInteger value, byte[] buffer, int offset, int length, byte pos, byte neg, byte zero ) { if ( value.bitLength() < 63 ) { // The value can be stored in a long, so use the method that doesn't // operate on BigIntegers. It's much faster than this one. toNum( value.longValue(), buffer, offset, length, pos, neg, zero ); return; } // Split the value into two BigIntegers that can be converted to longs. BigInteger[] topAndBottom = value.divideAndRemainder( TEN_TO_THE_SEVENTEENTH ); // Write the bottom half and sign using the other toNum method. int byteOfSeventeethDigit = offset + length - 17; long bottom = topAndBottom[ 1 ].longValue(); toNum( bottom, buffer, byteOfSeventeethDigit, 17, pos, neg, zero ); // Convert the top half to a long, and make sure it's positive since the // sign has already been written. long top = topAndBottom[ 0 ].longValue(); if ( top < 0 ) { top = -top; } // Write the top half of the digits. Start from the end (digit 18) and // work backwards. int digitsToWrite = length - 17; int bufferIndex = byteOfSeventeethDigit; for ( ; top > 0 && digitsToWrite > 0; digitsToWrite-- ) { bufferIndex--; buffer[ bufferIndex ] = (byte)((top % 10) + zero); top = top / 10; } // Fill with zeros if neccessary. while ( digitsToWrite > 0 ) { digitsToWrite--; bufferIndex--; buffer[ bufferIndex ] = zero; } } /** * Converts binary NUM/NUMC data from local format to remote format. * * @param source the source of the data. * @param sourceOffset where the data begins within source. * @param length how many digits we have. * @param isNum true if it's NUM data, false if it's NUMC data. * @param target where to put the result. * @param targetOffset where the result should begin within target. */ public static void numToRemote( byte[] source, int sourceOffset, int length, boolean isNum, byte[] target, int targetOffset ) { // Convert the digits. byte zero = Platform.IS_ASCII ? EBCDIC_ZERO_DIFF : ASCII_ZERO_DIFF; for ( int i = 0; i < length - 1; i++ ) { target[ targetOffset + i ] = (byte)(zero + (byte)(source[ sourceOffset + i ] & 0xF)); } target[ targetOffset + length - 1 ] = (byte)(source[ sourceOffset + length - 1 ] & 0xF); // Convert the sign (it's in the last byte). byte sign = (byte)(source[ sourceOffset + length - 1 ] & 0xF0); if ( Platform.IS_ASCII ) { // The data is in ASCII. if ( sign == ASCII_POSITIVE_NUM_NUMC_MASK ) { byte posSign = isNum ? EBCDIC_POSITIVE_NUM_MASK : EBCDIC_POSITIVE_NUMC_MASK; target[ targetOffset + length - 1 ] |= posSign; } else { target[ targetOffset + length - 1 ] |= EBCDIC_NEGATIVE_NUM_NUMC_MASK; } } else { // The data is in EBCDIC. if ( sign == EBCDIC_POSITIVE_NUM_MASK || sign == EBCDIC_POSITIVE_NUMC_MASK ) { target[ targetOffset + length - 1 ] |= ASCII_POSITIVE_NUM_NUMC_MASK; } else { target[ targetOffset + length - 1 ] |= ASCII_NEGATIVE_NUM_NUMC_MASK; } } } // IEEE 754 Floating point format represents numbers as follows: // seee eeee | efff ffff | ffff ffff | ffff ffff // where s = sign bit // e = eponent bit // f = fraction bit // The decimal point is assumed at the left of the second byte. // When the exponent is non-zero, the MSB of the second byte is assumed to be // value 1 (2^-1 = 0.5). private final static int FLOAT_SIGN_MASK = 0x80000000; private final static int FLOAT_EXPONENT_MASK = 0x7f800000; private final static int FLOAT_MANTISSA_MASK = 0x007fffff; private final static int FLOAT_MANTISSA_MSB_MASK = 0x00800000; // The exponent is treated as an unsigned number, giving a range of 0..255. // A bias of 126 is subtracted from it to give the power used. private final static int FLOAT_BIAS = 126; // The value of the IEEE Float is then given by mantissa * (2^(exp-126)), the // sign bit giving the sign // S390 floats have the following format: // seee eee | ffff ffff | ffff ffff | ffff ffff // The exponent a power of 16, not 2. Hence the best we can guarantee is that one // of the top four bits of the mantissa are non-zero - therefore there is no implied // bit as with IEEE // The exponent has a bias of 64, giving a range of -63..64 private final static int S390_FLOAT_BIAS = 64; private final static int S390_FLOAT_EXPONENT_MASK = 0x7f000000; private final static int S390_FLOAT_MANTISSA_MASK = 0x00ffffff; /** * Converts an IEEE float to an S390 float (as an int). * * @param ieeeFloat the IEEE float. * @return the number as an S390 float. */ public static int floatToS390IntBits( float ieeeFloat ) { // To convert from IEEE to S390 we use the following formula: // let r = exponent % 4; q = exponent / 4; // if q == 0 then m.2^x = m.16^q // if q != 0 then m.2^x = (m.2^(r-4)).16^(q+1) for positive q, // = (m.2^-r).16^q) for negative q // Get the bit pattern int ieeeIntBits = Float.floatToIntBits( ieeeFloat ); // Test the sign bit (0 = positive, 1 = negative) boolean positive = ((ieeeIntBits & FLOAT_SIGN_MASK) == 0); // Deal with zero straight away... if ( (ieeeIntBits & 0x7fffffff) == 0 ) { // + or - 0.0 return ieeeIntBits; } // Extract the exponent int exponent = ieeeIntBits & FLOAT_EXPONENT_MASK; // shift right 23 bits to get exponent in least significant byte exponent = exponent >>> 23; // subtract the bias to get the true value exponent = exponent - FLOAT_BIAS; // Extract the mantissa int mantissa = ieeeIntBits & FLOAT_MANTISSA_MASK; // for an exponent greater than -FLOAT_BIAS, add in the implicit bit if ( exponent > (-FLOAT_BIAS) ) { mantissa = mantissa | FLOAT_MANTISSA_MSB_MASK; } // Now begin the conversion to S390 int remainder = Math.abs( exponent ) % 4; int quotient = Math.abs( exponent ) / 4; int s390Exponent = quotient; if ( (exponent > 0) && (remainder != 0) ) { s390Exponent = s390Exponent + 1; } // put the sign back in if ( exponent < 0 ) { s390Exponent = -s390Exponent; } // Add the bias s390Exponent += S390_FLOAT_BIAS; // Now adjust the mantissa part int s390Mantissa = mantissa; if ( remainder > 0 ) { if ( exponent > 0 ) { // the next two lines perform the (m.2^(r-4)) part of the // conversion int shift_places = 4 - remainder; s390Mantissa = s390Mantissa >>> shift_places; } else { // to avoid loss of precision when the exponent is at a minimum, // we may need to shift the mantissa four places left, and // decrease the // s390Exponent by one before shifting right if ( (exponent == -(FLOAT_BIAS)) && ((s390Mantissa & 0x00f00000) == 0) ) { s390Mantissa = s390Mantissa << 4; s390Exponent = s390Exponent - 1; } // the next two line perform the (m.2^-r) part of the conversion int shift_places = remainder; s390Mantissa = s390Mantissa >>> shift_places; } } // An exponent of -FLOAT_BIAS is the smallest that IEEE can do. S390 has // a wider range, and hence may be able to normalise the mantissa more // than // is possible for IEEE // Also, since an exponent of -FLOAT_BIAS has no implicit bit set, the // mantissa // starts with a value of 2^-1 at the second bit of the second byte. We // thus need // to shift one place left to move the mantissa to the S390 position // Follwoing that, we notmalise as follows: // Each shift left of four bits is equivalent to multiplying by 16, // so the exponent must be reduced by 1 if ( exponent == -(FLOAT_BIAS) ) { s390Mantissa = s390Mantissa << 1; while ( (s390Mantissa != 0) && ((s390Mantissa & 0x00f00000) == 0) ) { s390Mantissa = s390Mantissa << 4; s390Exponent = s390Exponent - 1; } } // Assemble the s390BitPattern int s390Float = 0; int s390ExponentBits = s390Exponent & 0x0000007F; // make sure we only deal with 7 bits // add the exponent s390Float = s390ExponentBits << 24; // shift to MSB // add the sign if ( !positive ) { s390Float = s390Float | FLOAT_SIGN_MASK; } // add the mantissa s390Float = s390Float | s390Mantissa; return s390Float; } /** * Converts an S390 float (as an int) to an IEEE float. * * @param floatBits the S390 float. * @return the number as an IEEE float. */ public static float intS390BitsToFloat( int floatBits ) { // To convert from S390 to IEEE we use the fomula: // m.16^x = m.2^4x, and then normalise by shifting the mantissa up to // three places left // Test the sign bit (0 = positive, 1 = negative) boolean positive = ((floatBits & FLOAT_SIGN_MASK) == 0); // Deal with zero straight away... if ( (floatBits & 0x7fffffff) == 0 ) { // + or - 0.0 if ( positive ) { return 0.0F; } else { return -(0.0F); } } int mantissa = floatBits & S390_FLOAT_MANTISSA_MASK; int exponent = floatBits & S390_FLOAT_EXPONENT_MASK; // move the exponent into the LSB exponent = exponent >> 24; // subtract the bias exponent = exponent - S390_FLOAT_BIAS; // caculate the IEEE exponent int ieeeExponent = exponent * 4; // Normalise the mantissa int ieeeMantissa = mantissa; // Deal with exponents <= -FLOAT_BIAS if ( ieeeExponent <= -(FLOAT_BIAS) ) { // ieeeMantissa is one place to the right since there is no implicit // bit set ieeeMantissa = ieeeMantissa >> 1; // now increase the exponent until it reaches -FLOAT_BIAS, shifting // right one // place at each stage to compensate while ( ieeeExponent < -(FLOAT_BIAS) ) { ieeeExponent = ieeeExponent + 1; ieeeMantissa = ieeeMantissa >> 1; } } // Deal with exponents greater than -FLOAT_BIAS while ( (ieeeMantissa != 0) && ((ieeeMantissa & FLOAT_MANTISSA_MSB_MASK) == 0) && (ieeeExponent > -(FLOAT_BIAS)) ) { ieeeMantissa = ieeeMantissa << 1; // *2 ieeeExponent = ieeeExponent - 1; // /2 } // s390 has a wider range than IEEE, so deal with over and underflows if ( ieeeExponent < -149 ) { return 0.0F; // underflow } else { if ( ieeeExponent > 128 ) { if ( positive ) { return (Float.MAX_VALUE * 2); // + infinity } else { return -(Float.MAX_VALUE * 2); // -infinity } } } // Build the IEEE float int ieeeBits = 0; if ( !positive ) { ieeeBits = ieeeBits | FLOAT_SIGN_MASK; } // add the bias to the exponent ieeeExponent = ieeeExponent + FLOAT_BIAS; // move it to the IEEE exponent position ieeeExponent = ieeeExponent << 23; // add to the result ieeeBits = ieeeBits | ieeeExponent; // mask the top bit of the mantissa (implicit in IEEE) ieeeMantissa = ieeeMantissa & FLOAT_MANTISSA_MASK; // add to the result ieeeBits = ieeeBits | ieeeMantissa; return Float.intBitsToFloat( ieeeBits ); } // IEEE 754 Floating point format represents double precision numbers as follows: // seee eeee | eeee ffff | ffff ffff | ffff ffff | ...... // where s = sign bit // e = eponent bit // f = fraction bit // The decimal point is assumed at the left of the second byte. // When the exponent is non-zero, the MSB of the second byte is assumed to be // value 1 (2^-1 = 0.5). private final static long DOUBLE_SIGN_MASK = 0x8000000000000000L; private final static long DOUBLE_EXPONENT_MASK = 0x7ff0000000000000L; private final static long DOUBLE_MANTISSA_MASK = 0x000fffffffffffffL; private final static long DOUBLE_MANTISSA_MSB_MASK = 0x0010000000000000L; // The exponent is treated as an unsigned number // A bias of 1022 is subtracted from it to give the power used. private final static long DOUBLE_BIAS = 1022; // The value of the IEEE Float is then given by mantissa * (2^(exp-1022)), the // sign bit giving the sign // S390 double precision floats have the following format: // seee eee | ffff ffff | ffff ffff | ffff ffff | .... // The exponent a power of 16, not 2. Hence the best we can guarantee is that one // of the top four bits of the mantissa are non-zero - therefore there is no implied // bit as with IEEE // The exponent has a bias of 64, giving a range of -63..64 private final static int S390_DOUBLE_BIAS = 64; private final static long S390_DOUBLE_EXPONENT_MASK = 0x7f00000000000000L; private final static long S390_DOUBLE_MANTISSA_MASK = 0x00ffffffffffffffL; /** * Converts an IEEE double to an S390 double (as a long). * * @param ieeeDouble the IEEE double. * @return the number as an S390 double. */ public static long doubleToS390LongBits( double ieeeDouble ) { // To convert from IEEE to S390 we use the following formula: // let r = exponent % 4; q = exponent / 4; // if q == 0 then m.2^x = m.16^q // if q != 0 then m.2^x = (m.2^(r-4)).16^(q+1) for positive q, // = (m.2^-r).16^q) for negative q // Get the bit pattern long ieeeLongBits = Double.doubleToLongBits( ieeeDouble ); // Test the sign bit (0 = positive, 1 = negative) boolean positive = ((ieeeLongBits & DOUBLE_SIGN_MASK) == 0); // Deal with zero straight away... if ( (ieeeLongBits & 0x7fffffffffffffffL) == 0 ) { // + or - 0.0 return ieeeLongBits; } // Extract the exponent long exponent = ieeeLongBits & DOUBLE_EXPONENT_MASK; // shift right 52 bits to get exponent in least significant byte exponent = exponent >>> 52; // subtract the bias to get the true value exponent = exponent - DOUBLE_BIAS; // Extract the mantissa long mantissa = ieeeLongBits & DOUBLE_MANTISSA_MASK; // Now begin the conversion to S390 long remainder = Math.abs( exponent ) % 4; long quotient = Math.abs( exponent ) / 4; long s390Exponent = quotient; if ( (exponent > 0) && (remainder != 0) ) { s390Exponent = s390Exponent + 1; } // put the sign back in if ( exponent < 0 ) { s390Exponent = -s390Exponent; } // Add the bias s390Exponent += S390_DOUBLE_BIAS; // Now adjust the mantissa part long s390Mantissa = mantissa; // for an exponent greater than -DOUBLE_BIAS, add in the implicit bit if ( exponent > (-DOUBLE_BIAS) ) { s390Mantissa = s390Mantissa | DOUBLE_MANTISSA_MSB_MASK; } else { // there is no implicit bit, so the mantissa is one bit to the right // of what // we would normally expect. We need to fix this for S390 s390Mantissa = s390Mantissa << 1; } // S390 Mantissa starts 4 bits left of ieee one. The first of these is // implied in // IEEE so only shift 3 places s390Mantissa = s390Mantissa << 3; if ( remainder > 0 ) { if ( exponent > 0 ) { // the next two lines perform the (m.2^(r-4)) part of the // conversion int shift_places = (int)(4 - remainder); s390Mantissa = s390Mantissa >>> shift_places; } else { // to avoid loss of precision when the exponent is at a minimum, // we may need to shift the mantissa four places left and // decrease the // s390 exponent by one before shifting right if ( (exponent == -(DOUBLE_BIAS)) && ((s390Mantissa & 0x00f0000000000000L) == 0) ) { s390Mantissa = s390Mantissa << 4; s390Exponent = s390Exponent - 1; } // the next two lines perform the m.2-r part of the conversion s390Mantissa = s390Mantissa >>> remainder; } } // An exponent of -DOUBLE_BIAS is the smallest that IEEE can do. S390 // has // a wider range, and hence may be able to normalise the mantissa more // than // is possible for IEEE // Each shift left of four bits is equivalent to multiplying by 16, // so the exponent must be reduced by 1 if ( exponent == -(DOUBLE_BIAS) ) { while ( (s390Mantissa != 0) && ((s390Mantissa & 0x00f0000000000000L) == 0) ) { s390Mantissa = s390Mantissa << 4; s390Exponent = s390Exponent - 1; } } // if the exponent is now > 127, we have an overflow since IEEE can // handle larger numbers // than S390 can. if ( s390Exponent > 127 ) { throw new RuntimeException( //TODO need to make a proper AnyException for this "Number outside of range for double precision OS390 Float" ); } else if ( s390Exponent < 0 ) { // the number is too small to represent, set it to zero return 0L; } // Assemble the s390BitPattern long s390Double = 0L; long s390ExponentBits = s390Exponent & 0x000000000000007FL; // make sure we only deal with 7 bits // add the exponent s390Double = s390ExponentBits << 56; // shift to MSB // add the sign if ( !positive ) { s390Double = s390Double | DOUBLE_SIGN_MASK; } // add the mantissa s390Double = s390Double | s390Mantissa; return s390Double; } /** * Converts an S390 double (as a long) to an IEEE double. * * @param doubleBits the S390 double. * @return the number as an IEEE double. */ public static double longS390BitsToDouble( long doubleBits ) { // To convert from S390 to IEEE we use the fomula: // m.16^x = m.2^4x, and then normalise by shifting the mantissa up to // three // places left // Test the sign bit (0 = positive, 1 = negative) boolean positive = ((doubleBits & DOUBLE_SIGN_MASK) == 0); // Deal with zero straight away... if ( (doubleBits & 0x7fffffffffffffffL) == 0 ) { // + or - 0.0 if ( positive ) { return 0.0D; } else { return -(0.0D); } } long mantissa = doubleBits & S390_DOUBLE_MANTISSA_MASK; long exponent = doubleBits & S390_DOUBLE_EXPONENT_MASK; // move the exponent into the LSB exponent = exponent >> 56; // subtract the bias exponent = exponent - S390_DOUBLE_BIAS; // caculate the IEEE exponent long ieeeExponent = exponent * 4; // Normalise the mantissa long ieeeMantissa = mantissa; // IEEE mantissa starts three places right of S390 (+ implicit bit) ieeeMantissa = ieeeMantissa >> 3; // if this is the samllest possible exponent, then there is no implicit // bit, // and so we need to shift an extra bit if ( ieeeExponent <= -(DOUBLE_BIAS) ) { ieeeMantissa = ieeeMantissa >> 1; // now increase the exponent until it reaches -DOUBLE_BIAS, shifting // right one place at each stage to compensate while ( ieeeExponent < -(DOUBLE_BIAS) ) { ieeeExponent = ieeeExponent + 1; ieeeMantissa = ieeeMantissa >> 1; } } // complete the normalisation for exponents > -DOUBLE_BIAS while ( (ieeeMantissa != 0) && ((ieeeMantissa & DOUBLE_MANTISSA_MSB_MASK) == 0) && (ieeeExponent > -(DOUBLE_BIAS)) ) { ieeeMantissa = ieeeMantissa << 1; // *2 ieeeExponent = ieeeExponent - 1; // /2 } // s390 has a wider range than IEEE, so deal with over and underflows if ( ieeeExponent < -1045 ) { return 0.0F; // underflow } else { if ( ieeeExponent > 1024 ) { if ( positive ) { return (Double.MAX_VALUE * 2); // + infinity } else { return -(Double.MAX_VALUE * 2); // -infinity } } } // Built the IEEE double long ieeeBits = 0; if ( !positive ) { ieeeBits = ieeeBits | DOUBLE_SIGN_MASK; } // add the bias to the exponent ieeeExponent = ieeeExponent + DOUBLE_BIAS; // move it to the IEEE exponent position ieeeExponent = ieeeExponent << 52; // add to the result ieeeBits = ieeeBits | ieeeExponent; // mask the top bit of the mantissa (implicit in IEEE) ieeeMantissa = ieeeMantissa & DOUBLE_MANTISSA_MASK; // add to the result ieeeBits = ieeeBits | ieeeMantissa; return Double.longBitsToDouble( ieeeBits ); } }