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;
/**
* 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 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<BigFloat> {
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<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() {
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() {
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<BigFloat> {
}
/**
* {@code this=round(this)}
* Round this value to the nearest whole number
*/
public void round() {
BigFloat half = new BigFloat(fracbits, expbits, FloatKind.FINITE, +1,

View file

@ -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) {

View file

@ -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());
}
}