diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcode/floatformat/BigFloat.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcode/floatformat/BigFloat.java index 78074b1d63..44e67e3306 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcode/floatformat/BigFloat.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcode/floatformat/BigFloat.java @@ -48,16 +48,19 @@ public strictfp class BigFloat implements Comparable { int scale; /** - * Construct a BigFloat. If kind is FINITE, the value is sign*unscaled*2^(scale-fracbits) + * Construct a BigFloat. If kind is FINITE, the value is sign*unscaled*2^(scale-fracbits). + *

+ * NOTE: Requires that normal values are constructed in a normal form as with denormal values. * - * @param fracbits number of fractional bits - * @param expbits maximum number of bits in exponent + * @param fracbits number of fractional bits (positive non-zero value) + * @param expbits maximum number of bits in exponent (positive non-zero value) * @param kind the Kind, FINITE, INFINITE, ... * @param sign +1 or -1 - * @param unscaled the value's mantissa - * @param scale value's scale + * @param unscaled the value's mantissa (bit size <= fracbits+1) + * @param scale value's scale (signed value with the biased range of expbits) + * @throws IllegalArgumentException if invalid unscaled and scale values are specified based upon the fracbits and expbits values. */ - public BigFloat(int fracbits, int expbits, FloatKind kind, int sign, BigInteger unscaled, + BigFloat(int fracbits, int expbits, FloatKind kind, int sign, BigInteger unscaled, int scale) { this.fracbits = fracbits; this.expbits = expbits; @@ -68,6 +71,15 @@ public strictfp class BigFloat implements Comparable { this.maxScale = (1 << (expbits - 1)) - 1; this.minScale = 1 - this.maxScale; + + if (unscaled.bitLength() > (fracbits + 1)) { + throw new IllegalArgumentException("unscaled value exceeds " + (fracbits + 1) + + " bits in length (length=" + unscaled.bitLength() + ")"); + } + if (scale < minScale || scale > maxScale) { + throw new IllegalArgumentException( + "scale out of bounds " + minScale + " to " + maxScale + " (scale=" + scale + ")"); + } } @Override @@ -219,10 +231,28 @@ public strictfp class BigFloat implements Comparable { } /** - * @return {@code true} if this BigFloat is FINITE and normal + * Determine if the state of this BigFloat reflects a normalized value. + *

NOTE: This method relies on the manner of construction and + * only checks for {@link FloatKind#FINITE} and that full size of the + * fractional bits is used for the unscaled value. + * + * @return {@code true} if this BigFloat is FINITE and normal. */ public boolean isNormal() { - return kind == FloatKind.FINITE && unscaled.bitLength() >= fracbits + 1; + return kind == FloatKind.FINITE && unscaled.bitLength() == (fracbits + 1); + } + + /** + * Determine if the state of this BigFloat reflects a subnormal/denormal value. + *

NOTE: This method relies on the manner of construction and + * only checks for {@link FloatKind#FINITE} and that the non-zero + * unscaled valued does not use all fractional bits. + * + * @return {@code true} if this BigFloat is FINITE and denormal + */ + public boolean isDenormal() { + return kind == FloatKind.FINITE && !unscaled.equals(BigInteger.ZERO) && + unscaled.bitLength() <= fracbits; } /** @@ -293,14 +323,22 @@ public strictfp class BigFloat implements Comparable { public BigDecimal toBigDecimal() { switch (kind) { case FINITE: - // sign * unscaled * 2^(scale-fracbits) + if (isZero()) { + return BigDecimal.ZERO; + } + int unusedBits = Math.max(unscaled.getLowestSetBit(), 0); + BigInteger val = unscaled; int iscale = scale - fracbits; BigDecimal x; - if (iscale >= 0) { - x = new BigDecimal(unscaled.shiftLeft(iscale)); + if (iscale >= -unusedBits) { + x = new BigDecimal(val.shiftLeft(iscale)); } else { - x = new BigDecimal(unscaled.multiply(BigInteger.valueOf(5).pow(-iscale)), + if (unusedBits > 0) { + val = unscaled.shiftRight(unusedBits); + iscale += unusedBits; + } + x = new BigDecimal(val.multiply(BigInteger.valueOf(5).pow(-iscale)), -iscale); } if (sign < 0) { @@ -941,7 +979,7 @@ public strictfp class BigFloat implements Comparable { } /** - * {@code this=round(this)} + * Round this value to the nearest whole number */ public void round() { BigFloat half = new BigFloat(fracbits, expbits, FloatKind.FINITE, +1, diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcode/floatformat/FloatFormat.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcode/floatformat/FloatFormat.java index f0d2b98687..9c3034161b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcode/floatformat/FloatFormat.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcode/floatformat/FloatFormat.java @@ -253,7 +253,7 @@ public strictfp class FloatFormat { return setSign(res, sgn); } - public Object getBigZero(boolean sgn) { + public BigFloat getBigZero(boolean sgn) { return new BigFloat(frac_size, exp_size, FloatKind.FINITE, sgn ? -1 : +1, BigInteger.ZERO, 2 - (1 << (exp_size - 1))); } @@ -404,11 +404,9 @@ public strictfp class FloatFormat { } else if (exp == maxexponent) { if (frac.signum() == 0) { // Floating point infinity - return new BigFloat(frac_size, exp_size, FloatKind.INFINITE, sign, BigInteger.ZERO, - maxexponent); + return BigFloat.infinity(frac_size, exp_size, sign); } - return new BigFloat(frac_size, exp_size, FloatKind.QUIET_NAN, sign, BigInteger.ZERO, - maxexponent); + return BigFloat.quietNaN(frac_size, exp_size, sign); } if (jbitimplied) { diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/pcode/floatformat/BigFloatTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/pcode/floatformat/BigFloatTest.java index 7645cc0f8e..ec1c75c324 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/pcode/floatformat/BigFloatTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/pcode/floatformat/BigFloatTest.java @@ -17,6 +17,7 @@ package ghidra.pcode.floatformat; import static org.junit.Assert.*; +import java.math.BigDecimal; import java.util.List; import java.util.Random; import java.util.stream.Collectors; @@ -358,4 +359,44 @@ public class BigFloatTest extends AbstractGenericTest { unaryDoubleOpTest(a -> Math.ceil(a), a -> a.ceil()); } + @Test + public void testToBigDecimal() { + + assertEquals(BigDecimal.ZERO, FloatFormat.toBigFloat(0.0d).toBigDecimal()); + assertEquals(BigDecimal.ONE, FloatFormat.toBigFloat(1.0d).toBigDecimal()); + assertEquals(BigDecimal.ONE.negate(), FloatFormat.toBigFloat(-1.0d).toBigDecimal()); + + assertEquals(new BigDecimal(Double.MIN_VALUE), + FloatFormat.toBigFloat(Double.MIN_VALUE).toBigDecimal()); + assertEquals(new BigDecimal(Double.MIN_NORMAL), + FloatFormat.toBigFloat(Double.MIN_NORMAL).toBigDecimal()); + assertEquals(new BigDecimal(Double.MAX_VALUE), + FloatFormat.toBigFloat(Double.MAX_VALUE).toBigDecimal()); + + assertEquals(new BigDecimal(0.5d), FloatFormat.toBigFloat(0.5d).toBigDecimal()); + assertEquals(new BigDecimal(2.5d), FloatFormat.toBigFloat(2.5d).toBigDecimal()); + assertEquals(new BigDecimal(-0.5d), FloatFormat.toBigFloat(-0.5d).toBigDecimal()); + assertEquals(new BigDecimal(-2.5d), FloatFormat.toBigFloat(-2.5d).toBigDecimal()); + } + + @Test + public void testNormalAndDenormal() { + + BigFloat bf = FloatFormat.toBigFloat(0.0d); + assertFalse(bf.isNormal()); + assertFalse(bf.isDenormal()); + + bf = FloatFormat.toBigFloat(Double.MIN_NORMAL); + assertTrue(bf.isNormal()); + assertFalse(bf.isDenormal()); + + bf = FloatFormat.toBigFloat(Double.MAX_VALUE); + assertTrue(bf.isNormal()); + assertFalse(bf.isDenormal()); + + bf = FloatFormat.toBigFloat(Double.MIN_VALUE); + assertFalse(bf.isNormal()); + assertTrue(bf.isDenormal()); + } + }