/******************************************************************************* * Copyright (c) 2012 Wind River Systems, Inc. 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: * Randy Rohrbach (Wind River Systems, Inc.) - Copied and modified to create the floating point plugin *******************************************************************************/ package org.eclipse.cdt.debug.ui.memory.floatingpoint; import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.Arrays; import java.util.regex.Pattern; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.progress.UIJob; public class FPutilities { private static final int BYTE_MASK = 0xFF; // ANSI C "Smallest" and "largest" negative and positive float and double values public static final float floatNegMax = -3.40282347E+38f; // Largest negative float value; farthest from zero public static final float floatNegMin = -1.17549435E-38f; // Smallest negative float value; closest to zero public static final float floatPosMax = 1.17549435E+38f; // Largest positive float value; farthest from zero public static final float floatPosMin = 3.40282347E-38f; // Smallest positive float value; closest to zero public static final double doubleNegMax = -1.7976931348623157E+308; // Largest positive double value public static final double doubleNegMin = -2.2250738585072014E-308; // Smallest positive double value public static final double doublePosMax = 1.7976931348623157E+308; // Largest positive double value public static final double doublePosMin = 2.2250738585072014E-308; // Smallest positive double value public enum FPDataType { // Value (for persisteance), Bitsize of type, Number of internal precision decimal digits, Default displayed precision FLOAT ( 10, 32, 7, 8), // C/C++ single-precision "float" DOUBLE ( 20, 64, 15, 8), // C/C++ double-precision "double" FLOAT_80 ( 30, 80, 19, 16), // Extended precision FLOAT_96 ( 40, 96, 0, 0), // TODO: unknown internal decimal digit precision; C/C++ extended-precision "long double" // Future work FLOAT_128 (50, 128, 33, 16), // TODO: known values, but not currently implmented FLOAT_256 (60, 256, 0, 0), // TODO: unknown internal decimal digit precision FLOAT_512 (70, 512, 0, 0); // TODO: unknown internal decimal digit precision // Member variables private int value; private int bitsize; private int decimalPrecision; private int displayedPrecision; // Constructor private FPDataType(int value, int bitSize, int precisionDigits, int defaultDisplayPrecision) { this.value = value; this.bitsize = bitSize; this.decimalPrecision = precisionDigits; this.displayedPrecision = defaultDisplayPrecision; } // Getters public int getValue() { return value; } public int getBitsize() { return bitsize; } public int getDecimalPrecision() { return decimalPrecision; } public int getDisplayedPrecision() { return displayedPrecision; } public int getInternalPrecision() { return decimalPrecision; } public int getByteLength() { return bitsize/Byte.SIZE; } } // Byte ordering public enum Endian { // Value LITTLE (10), BIG (20); // Member variables private int value; // Constructor private Endian(int value) { this.value = value; } // Getters public int getValue() { return value; } } // Justification (latent support) public enum Justification { LEFT, RIGHT, CENTER; } // Convert raw float bits to a byte array public static byte[] rawFloatBitsToByteArray(int floatBits) { int byteCount = Integer.SIZE/Byte.SIZE; byte[] result = new byte[byteCount]; for (int index = 0; index < byteCount; index++) { int offset = (result.length - 1 - index) * 8; result[index] = (byte) ((floatBits >>> offset) & BYTE_MASK); } return result; } // Convert raw double bits to a byte array public static byte[] rawDoubleBitsToByteArray(long doubleBits) { int byteCount = Long.SIZE/Byte.SIZE; byte[] result = new byte[byteCount]; for (int index = 0; index < byteCount; index++) { int offset = (result.length - 1 - index) * 8; result[index] = (byte) ((doubleBits >>> offset) & BYTE_MASK); } return result; } // Return a byte array that is in reverse order of the passed-in array parameter public static byte[] reverseByteOrder(byte[] byteArray) { if (byteArray.length == 0) return new byte[0]; byte tempByte = 0; byte[] reversedByteArray = new byte[byteArray.length]; // Copy the array that is passed in to the array that will be returned System.arraycopy(byteArray, 0, reversedByteArray, 0, byteArray.length); // Reverse the bytes for(int start = 0, end = reversedByteArray.length - 1; start < end; ++start, --end) { tempByte = reversedByteArray[start]; reversedByteArray[start] = reversedByteArray[end]; reversedByteArray[end] = tempByte; } return reversedByteArray; } // Convert a representation of a float or double in a byte array to a scientific notation string (Should we use BigDecimal here???) public static String byteArrayToSciNotation(FPDataType dt, boolean isLittleEndian, FPMemoryByte[] memByteArray, int maxDisplayDigits) throws ArithmeticException { int displayedDigits = 8; // If the byte array is not a 32-bit float or 64-bit double, throw an exception. if (memByteArray.length != (FPDataType.FLOAT.getByteLength()) && memByteArray.length != (FPDataType.DOUBLE.getByteLength())) throw new ArithmeticException("Conversion of the floating point number cannot be performed; invalid data type or byte array length."); //$NON-NLS-1$ // Create and initialize a DecimalFormat object for scientific notation. Specify a space // for the preceding plus-sign, which lines up the first significant digit, decimal point // and exponent character. Define the symbol strings for "Not a Number" and "Infinity." DecimalFormat df = new DecimalFormat("0.0E0"); //$NON-NLS-1$ df.setPositivePrefix(" "); //$NON-NLS-1$ DecimalFormatSymbols dfSymbols = new DecimalFormatSymbols(); dfSymbols.setNaN(" "+ FPRenderingMessages.getString("FPRendering.NAN")); //$NON-NLS-1$ //$NON-NLS-2$ dfSymbols.setInfinity(FPRenderingMessages.getString("FPRendering.INFINITY")); //$NON-NLS-1$ df.setDecimalFormatSymbols(dfSymbols); // Set the integer and fraction digits for normalized scientific notation. df.setMinimumIntegerDigits(1); df.setMaximumIntegerDigits(1); if (dt == FPDataType.FLOAT) displayedDigits = Math.min(maxDisplayDigits, FPDataType.FLOAT.getInternalPrecision()); if (dt == FPDataType.DOUBLE) displayedDigits = Math.min(maxDisplayDigits, FPDataType.DOUBLE.getInternalPrecision()); df.setMinimumFractionDigits(displayedDigits - 1); df.setMaximumFractionDigits(displayedDigits - 1); // Convert the byte array to a scientific notation floating point number string (only floats and doubles currently supported) ByteOrder byteOrder = isLittleEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN; return df.format(dt == FPDataType.FLOAT ? ByteBuffer.wrap(memoryBytesToByteArray(memByteArray)).order(byteOrder).getFloat() : ByteBuffer.wrap(memoryBytesToByteArray(memByteArray)).order(byteOrder).getDouble()); } // Convert a floating point string to a byte array (*** only 'floats' and 'doubles' currently supported ***) public static byte[] floatingStringToByteArray(FPDataType dt, String valueString, int dataTypeBitCount) throws NumberFormatException { // Remove whitespace and check for non-zero length valueString = valueString.trim().replaceAll(" ", ""); //$NON-NLS-1$ //$NON-NLS-2$ if (valueString.length() != 0) { // Float handling if (dt == FPDataType.FLOAT || FPDataType.FLOAT.getBitsize() == dataTypeBitCount) { // Convert the string to a float. Check the range. Convert to byte array. float floatValue = new Float(valueString).floatValue(); floatValue = floatLimitCheck(floatValue); return rawFloatBitsToByteArray(Float.floatToRawIntBits(floatValue)); } // Double handling if (dt == FPDataType.DOUBLE || FPDataType.DOUBLE.getBitsize() == dataTypeBitCount) { // Convert the string to a double. Check the range. Convert to byte array. double doubleValue = new Double(valueString).doubleValue(); doubleValue = doubleLimitCheck(doubleValue); return rawDoubleBitsToByteArray(Double.doubleToRawLongBits(doubleValue)); } } return new byte[0]; } // Convert from an FPMemoryByte array to a byte array public static byte[] memoryBytesToByteArray(FPMemoryByte[] memoryByteArray) { byte[] byteArray = new byte[memoryByteArray.length]; for (int index = 0; index < memoryByteArray.length; index++) byteArray[index] = memoryByteArray[index].getValue(); return byteArray; } // Convert from a byte array to a MemoryByte array public static FPMemoryByte[] byteArrayToMemoryBytes(Endian endian, byte[] byteArray) { FPMemoryByte[] memoryBytes = new FPMemoryByte[byteArray.length]; for (int index = 0; index < byteArray.length; index++) { memoryBytes[index] = new FPMemoryByte(); memoryBytes[index].setBigEndian(endian == Endian.BIG); memoryBytes[index].setValue(byteArray[index]); } return memoryBytes; } // Check the character for being valid for number entry, both standard and scientific notation public static boolean validEditCharacter(char character) { return (character >= '0' && character <= '9') || character == '+' || character == '-' || character == 'e' || character == 'E' || character == '.' || character == ' '; } // Validate floating point number string public static boolean isValidFormat(String string) { // Rules: // - A minimum of one digit preceding the optional exponent character is required. // - Allowable characters: 0-9, a decimal point, '+' and '-' number // signs, exponent characters 'e' and 'E', and spaces. // // Strings may also have: // - One [optional] decimal point // - A maximum of two [optional] number signs (one before the number and one after the exponent character) // - Only one [optional] exponent character is allowed boolean digit = false; char[] charArray = string.toCharArray(); // Phase I check: String scientificNotationPattern = "^[-+]??(\\d++[.]\\d*?|[.]?\\d+?|\\d+(?=[eE]))([eE][-+]??\\d++)?$"; //$NON-NLS-1$ if (!Pattern.matches(scientificNotationPattern, string)) return false; // Phase II check for (int index = 0; index < string.length(); index++) { // Check for a digit if (charArray[index] >= '0' && charArray[index] <= '9') digit = true; // Make sure it's a valid/allowable character if (!validEditCharacter(charArray[index])) return false; // Only one decimal point and exponent character is allowed if (FPutilities.countMatches(string.toLowerCase(), ".") > 1 || FPutilities.countMatches(string.toLowerCase(), "e") > 1) //$NON-NLS-1$ //$NON-NLS-2$ return false; // Number signs are only allowed in the first position and following the exponent character. if (((charArray[index] == '+' || charArray[index] == '-') && index != 0) && (charArray[index-1] != 'e' && charArray[index-1] != 'E')) return false; // Decimal points are not allowed after the exponent character int eIndex = string.toLowerCase().indexOf('e'); if (charArray[index] == '.' && eIndex != -1 && eIndex < index) return false; } return digit; } // Return a string of the specified length filled with the specified character public static String fillString(int length, char character) { if (length < 1) return ""; //$NON-NLS-1$ char[] charArray = new char[length]; Arrays.fill(charArray, character); return new String(charArray); } // Count the 'subString' matches in 'string' public static int countMatches(String string, String subString) { if (string.length() == 0 || subString.length() == 0) return 0; int count = 0; int index = 0; while ((index = string.indexOf(subString, index)) != -1) { count++; index += subString.length(); } return count; } // Print out a stack trace; useful for UI operations where stopping at a breakpoint causes button press context to be lost public static void stackTrace(int depth) { int offset = 3; // Ignore frames contributed to the stack based on call to this method if (depth == 0) depth = 4; // Default depth if zero supplied // Get the stack frames for the current thread; start at the offset StackTraceElement[] seArray = Thread.currentThread().getStackTrace(); if (seArray.length > offset) { System.out.println("Displaying " + depth + " of " + seArray.length + " stack trace elements"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ for (int index = offset; index < Math.min(depth + offset, seArray.length + offset); index++) System.out.println(" " + seArray[index].getClassName() + "." + seArray[index].getMethodName() + ": line " + seArray[index].getLineNumber()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } else System.out.println("No stack frames to display"); //$NON-NLS-1$ } // Pop up a message inside the UI thread public static void popupMessage(final String title, final String errorText, final Status status) { UIJob job = new UIJob("Floating Point Renderer") //$NON-NLS-1$ { // Notify the user of some condition via a pop-up box. @Override public IStatus runInUIThread(IProgressMonitor monitor) { ErrorDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), title, errorText, status); return Status.OK_STATUS; } }; job.setSystem(true); job.schedule(); } // Check float range. Returns -Infinity, the original value or +Infinity public static float floatLimitCheck(float floatValue) { if (floatValue != 0.0f && floatValue != Float.NEGATIVE_INFINITY && floatValue != Float.POSITIVE_INFINITY) { if (floatValue < 0) { if (Float.compare(floatValue, floatNegMax) < 0 || Float.compare(floatValue, floatNegMin) > 0) return Float.NEGATIVE_INFINITY; } else { if (Float.compare(floatValue, floatPosMin) < 0 || Float.compare(floatValue, floatPosMax) > 0) return Float.POSITIVE_INFINITY; } } return floatValue; } // Check double range. Returns a value of RangeCheck public static double doubleLimitCheck(double doubleValue) { if (doubleValue != 0.0 && doubleValue != Double.NEGATIVE_INFINITY && doubleValue != Double.POSITIVE_INFINITY) { if (doubleValue < 0) { if (Double.compare(doubleValue, doubleNegMax) < 0 || Double.compare(doubleValue, doubleNegMin) > 0) return Double.NEGATIVE_INFINITY; } else { if (Double.compare(doubleValue, doublePosMin) < 0 || Double.compare(doubleValue, doublePosMax) > 0) return Double.POSITIVE_INFINITY; } } return doubleValue; } // Convert a BigInteger to a hex String and return only the ending number of specified digits. public static String bi2HexStr(BigInteger bi, int lastDigits) { final int PAD_LENGTH = 12; String base16 = bi.toString(16); base16 = fillString(PAD_LENGTH - base16.length(), '0') + base16; return "0x" + base16.substring(PAD_LENGTH - lastDigits).toUpperCase(); //$NON-NLS-1$ } // Convert a BigInteger to a decimal String and return only the ending number of // specified digits. For example: bi2HexStr(239248506, 5) = "48506" public static String bi2DecStr(BigInteger bi, int lastDigits) { final int PAD_LENGTH = 12; String base10 = bi.toString(); base10 = fillString(PAD_LENGTH - base10.length(), '0') + base10; return base10.substring(PAD_LENGTH - lastDigits); } }