Merge remote-tracking branch 'origin/GP-1850_ghidra1_BigFloat_Fixes--SQUASHED'

This commit is contained in:
Ryan Kurtz 2022-04-14 15:19:59 -04:00
commit f89cf0c62e
3 changed files with 95 additions and 18 deletions

View file

@ -48,16 +48,19 @@ public strictfp class BigFloat implements Comparable<BigFloat> {
int scale; int scale;
/** /**
* Construct a BigFloat. If kind is FINITE, the value is <code>sign*unscaled*2^(scale-fracbits)</code> * Construct a BigFloat. If kind is FINITE, the value is <code>sign*unscaled*2^(scale-fracbits)</code>.
* <p>
* NOTE: Requires that normal values are constructed in a normal form as with denormal values.
* *
* @param fracbits number of fractional bits * @param fracbits number of fractional bits (positive non-zero value)
* @param expbits maximum number of bits in exponent * @param expbits maximum number of bits in exponent (positive non-zero value)
* @param kind the Kind, FINITE, INFINITE, ... * @param kind the Kind, FINITE, INFINITE, ...
* @param sign +1 or -1 * @param sign +1 or -1
* @param unscaled the value's mantissa * @param unscaled the value's mantissa (bit size <= fracbits+1)
* @param scale value's scale * @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) { int scale) {
this.fracbits = fracbits; this.fracbits = fracbits;
this.expbits = expbits; this.expbits = expbits;
@ -68,6 +71,15 @@ public strictfp class BigFloat implements Comparable<BigFloat> {
this.maxScale = (1 << (expbits - 1)) - 1; this.maxScale = (1 << (expbits - 1)) - 1;
this.minScale = 1 - this.maxScale; 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 @Override
@ -219,10 +231,28 @@ public strictfp class BigFloat implements Comparable<BigFloat> {
} }
/** /**
* @return {@code true} if this BigFloat is FINITE and normal * Determine if the state of this BigFloat reflects a normalized value.
* <p>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() { 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.
* <p>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<BigFloat> {
public BigDecimal toBigDecimal() { public BigDecimal toBigDecimal() {
switch (kind) { switch (kind) {
case FINITE: 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; int iscale = scale - fracbits;
BigDecimal x; BigDecimal x;
if (iscale >= 0) { if (iscale >= -unusedBits) {
x = new BigDecimal(unscaled.shiftLeft(iscale)); x = new BigDecimal(val.shiftLeft(iscale));
} }
else { 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); -iscale);
} }
if (sign < 0) { if (sign < 0) {
@ -941,7 +979,7 @@ public strictfp class BigFloat implements Comparable<BigFloat> {
} }
/** /**
* {@code this=round(this)} * Round this value to the nearest whole number
*/ */
public void round() { public void round() {
BigFloat half = new BigFloat(fracbits, expbits, FloatKind.FINITE, +1, BigFloat half = new BigFloat(fracbits, expbits, FloatKind.FINITE, +1,

View file

@ -253,7 +253,7 @@ public strictfp class FloatFormat {
return setSign(res, sgn); 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, return new BigFloat(frac_size, exp_size, FloatKind.FINITE, sgn ? -1 : +1, BigInteger.ZERO,
2 - (1 << (exp_size - 1))); 2 - (1 << (exp_size - 1)));
} }
@ -404,11 +404,9 @@ public strictfp class FloatFormat {
} }
else if (exp == maxexponent) { else if (exp == maxexponent) {
if (frac.signum() == 0) { // Floating point infinity if (frac.signum() == 0) { // Floating point infinity
return new BigFloat(frac_size, exp_size, FloatKind.INFINITE, sign, BigInteger.ZERO, return BigFloat.infinity(frac_size, exp_size, sign);
maxexponent);
} }
return new BigFloat(frac_size, exp_size, FloatKind.QUIET_NAN, sign, BigInteger.ZERO, return BigFloat.quietNaN(frac_size, exp_size, sign);
maxexponent);
} }
if (jbitimplied) { if (jbitimplied) {

View file

@ -17,6 +17,7 @@ package ghidra.pcode.floatformat;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -358,4 +359,44 @@ public class BigFloatTest extends AbstractGenericTest {
unaryDoubleOpTest(a -> Math.ceil(a), a -> a.ceil()); 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());
}
} }