blob: 76fc092cb3d2e71857969ee30d3e21c01dc84871 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2014 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:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.internal.debug.ui.model;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
public class TCFNumberFormat {
public static String isValidHexNumber(String s) {
int l = s.length();
if (l == 0) return "Need at least one digit";
for (int i = 0; i < l; i++) {
char ch = s.charAt(i);
if (ch >= '0' && ch <= '9') continue;
if (ch >= 'A' && ch <= 'F') continue;
if (ch >= 'a' && ch <= 'f') continue;
return "Hex digit expected";
}
return null;
}
public static String isValidDecNumber(boolean fp, String s) {
int i = 0;
int l = s.length();
if (l == 0) return "Need at least one digit";
char ch = s.charAt(i++);
if (ch == '-' || ch == '+') {
if (i >= l) return "Need at least one digit";
ch = s.charAt(i++);
}
if (fp) {
String n = s.substring(i - 1);
if (n.equals("NaN")) return null;
if (n.equals("Infinity")) return null;
}
while (ch >= '0' && ch <= '9') {
if (i >= l) return null;
ch = s.charAt(i++);
}
if (fp) {
if (ch == '.') {
if (i >= l) return null;
ch = s.charAt(i++);
while (ch >= '0' && ch <= '9') {
if (i >= l) return null;
ch = s.charAt(i++);
}
}
if (ch == 'e' || ch == 'E') {
if (i >= l) return "Invalid exponent: need at least one digit";
ch = s.charAt(i++);
if (ch == '-' || ch == '+') {
if (i >= l) return "Invalid exponent: need at least one digit";
ch = s.charAt(i++);
}
while (ch >= '0' && ch <= '9') {
if (i >= l) return null;
ch = s.charAt(i++);
}
return "Invalid exponent: decimal digit expected";
}
}
return "Decimal digit expected";
}
public static byte[] toByteArray(String s, int radix, boolean fp, int size, boolean signed, boolean big_endian) throws Exception {
byte[] bf = null;
boolean sign_extention = false;
if (!fp) {
BigInteger n = new BigInteger(s, radix);
sign_extention = n.signum() < 0;
bf = n.toByteArray();
}
else if (size == 4) {
int n = Float.floatToIntBits(Float.parseFloat(s));
bf = new byte[size];
for (int i = 0; i < size; i++) {
bf[i] = (byte)((n >> ((size - 1 - i) * 8)) & 0xff);
}
}
else if (size == 8) {
long n = Double.doubleToLongBits(Double.parseDouble(s));
bf = new byte[size];
for (int i = 0; i < size; i++) {
bf[i] = (byte)((n >> ((size - 1 - i) * 8)) & 0xff);
}
}
else if (size == 2 || size == 10 || size == 16) {
BigDecimal d = new BigDecimal(s);
int n = 0;
int bin_scale = 0;
for (n = 0; n < 1000; n++) {
d = d.stripTrailingZeros();
int scale = d.scale();
if (scale > 0) {
int x = d.precision();
if (x > 36) {
x -= 36;
if (x > scale) x = scale;
d = d.setScale(scale - x, RoundingMode.HALF_DOWN);
continue;
}
}
if (scale < 0) {
d = d.divide(BigDecimal.valueOf(2).pow(-scale));
bin_scale += scale;
}
else if (scale > 0) {
d = d.multiply(BigDecimal.valueOf(2).pow(scale));
bin_scale += scale;
}
else {
break;
}
}
BigInteger man = d.unscaledValue();
int cmp = man.compareTo(BigInteger.ZERO);
bf = new byte[size];
if (cmp != 0) {
boolean sign = cmp < 0;
if (sign) man = man.negate();
int man_bits = man.bitLength();
int man_offs = 0;
int exp = 0;
for (;;) {
if (size == 2) {
exp = man_bits - bin_scale + 14;
if (exp <= 0) {
man_bits += 1 - exp;
exp = 0;
}
if (exp > 0x1f) exp = 0x1f;
man_offs = 5;
}
else {
exp = man_bits - bin_scale + 16382;
if (exp <= 0) {
man_bits += 1 - exp;
exp = 0;
}
else if (size == 10) {
man_bits++;
}
if (exp > 0x7fff) exp = 0x7fff;
man_offs = 15;
}
// Rounding
int rb = man_offs + man_bits - size * 8 - 1;
if (rb >= 0 && man.testBit(rb)) {
man = man.add(BigInteger.ONE.shiftLeft(rb));
man_bits = man.bitLength();
}
else {
break;
}
}
if (sign) bf[0] |= 0x80;
for (int i = 1; i <= man_offs; i++) {
if (((1 << (man_offs - i)) & exp) != 0) {
bf[i / 8] |= (1 << (7 - i % 8));
}
}
for (int i = 0; i < man_bits; i++) {
int j = man_offs + i; // bit pos in bf
int k = man_bits - i - 1; // bit pos in man
if (j / 8 >= bf.length) break;
if (i == 0) {
assert man.testBit(k) == (exp > 0 && size != 10);
}
else if (man.testBit(k)) {
bf[j / 8] |= (1 << (7 - j % 8));
}
}
}
}
else {
throw new Exception("Unsupported floating point format");
}
byte[] rs = new byte[size];
if (rs.length > bf.length && sign_extention) {
// It is easier to fill all bytes instead of checking big_endian
for (int i = 0; i < rs.length; i++) rs[i] = (byte)0xff;
}
for (int i = 0; i < bf.length; i++) {
// i == 0 -> least significant byte
byte b = bf[bf.length - i - 1];
int j = big_endian ? rs.length - i - 1 : i;
if (j >= 0 && j < rs.length) rs[j] = b;
}
return rs;
}
public static String toFPString(byte[] data, boolean big_endian) {
return toFPString(data, 0, data.length, big_endian);
}
public static String toFPString(byte[] data, int offs, int size, boolean big_endian) {
assert offs + size <= data.length;
if (size == 12) {
// padded 80-bit extended precision on IA32
size = 10;
}
byte[] arr = new byte[size];
if (big_endian) {
System.arraycopy(data, offs, arr, 0, size);
}
else {
for (int i = 0; i < size; i++) {
arr[arr.length - i - 1] = data[offs + i];
}
}
boolean neg = (arr[0] & 0x80) != 0;
arr[0] &= 0x7f;
int precision = 0;
int exponent = 0;
boolean nan = false;
switch (size) {
case 2:
precision = 3;
exponent = (arr[0] & 0x7c) >> 2;
nan = exponent == 0x1f;
arr[0] &= 0x03;
if (exponent == 0) exponent = 1;
else if (!nan) arr[0] |= 0x04;
exponent -= 10; // Significand
exponent -= 15; // Exponent bias
break;
case 4:
precision = 7;
exponent = ((arr[0] & 0x7f) << 1) | ((arr[1] & 0x80) >> 7);
nan = exponent == 0xff;
arr[0] = 0;
arr[1] &= 0x7f;
if (exponent == 0) exponent = 1;
else if (!nan) arr[1] |= 0x80;
exponent -= 23; // Significand
exponent -= 127; // Exponent bias
break;
case 8:
precision = 16;
exponent = ((arr[0] & 0x7f) << 4) | ((arr[1] & 0xf0) >> 4);
nan = exponent == 0x7ff;
arr[0] = 0;
arr[1] &= 0x0f;
if (exponent == 0) exponent = 1;
else if (!nan) arr[1] |= 0x10;
exponent -= 52; // Significand
exponent -= 1023; // Exponent bias
break;
case 10:
case 16:
precision = 34;
exponent = ((arr[0] & 0x7f) << 8) | (arr[1] & 0xff);
nan = exponent == 0x7fff;
arr[0] = arr[1] = 0;
if (size == 10) {
exponent -= 63; // Significand
if (nan) arr[2] &= 0x7f;
}
else {
if (exponent == 0) exponent = 1;
else if (!nan) arr[1] = 1;
exponent -= 112; // Significand
}
exponent -= 16383; // Exponent bias
break;
default:
return "Unsupported floating point format";
}
if (nan) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] != 0) return neg ? "-NaN" : "+NaN";
}
return neg ? "-Infinity" : "+Infinity";
}
BigDecimal a = new BigDecimal(new BigInteger(arr), 0);
if (a.signum() != 0 && exponent != 0) {
BigDecimal p = new BigDecimal(BigInteger.valueOf(2), 0);
if (exponent > 0) {
a = a.multiply(p.pow(exponent));
}
else {
BigDecimal b = p.pow(-exponent);
a = a.divide(b, b.precision(), RoundingMode.HALF_DOWN);
}
if (precision != 0 && a.precision() > precision) {
int scale = a.scale() - a.precision() + precision;
a = a.setScale(scale, RoundingMode.HALF_DOWN);
}
}
String s = a.toString();
if (neg) s = "-" + s;
return s;
}
public static String toComplexFPString(byte[] data, boolean big_endian) {
return toComplexFPString(data, 0, data.length, big_endian);
}
public static String toComplexFPString(byte[] data, int offs, int size, boolean big_endian) {
int fp_size = size / 2;
StringBuffer bf = new StringBuffer();
bf.append(toFPString(data, offs, fp_size, big_endian));
String i = toFPString(data, offs + fp_size, fp_size, big_endian);
if (!i.equals("0")) {
if (!i.startsWith("-")) bf.append('+');
bf.append(i);
bf.append('i');
}
return bf.toString();
}
public static BigInteger toBigInteger(byte[] data, boolean big_endian, boolean sign_extension) {
return toBigInteger(data, 0, data.length, big_endian, sign_extension);
}
public static BigInteger toBigInteger(byte[] data, int offs, int size, boolean big_endian, boolean sign_extension) {
assert offs + size <= data.length;
byte[] temp = null;
if (sign_extension) {
temp = new byte[size];
}
else {
temp = new byte[size + 1];
temp[0] = 0; // Extra byte to avoid sign extension by BigInteger
}
if (big_endian) {
System.arraycopy(data, offs, temp, sign_extension ? 0 : 1, size);
}
else {
for (int i = 0; i < size; i++) {
temp[temp.length - i - 1] = data[i + offs];
}
}
return new BigInteger(temp);
}
}