ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/float.cc

673 lines
18 KiB
C++

/* ###
* IP: GHIDRA
* NOTE: uses some windows and sparc specific floating point definitions
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "float.hh"
#include "address.hh"
#include <cmath>
#include <limits>
namespace ghidra {
using std::ldexp;
using std::frexp;
using std::signbit;
using std::sqrt;
using std::floor;
using std::ceil;
using std::round;
using std::fabs;
/// Set format for a given encoding size according to IEEE 754 standards
/// \param sz is the size of the encoding in bytes
FloatFormat::FloatFormat(int4 sz)
{
size = sz;
if (size == 4) {
signbit_pos = 31;
exp_pos = 23;
exp_size = 8;
frac_pos = 0;
frac_size = 23;
bias = 127;
jbitimplied = true;
}
else if (size == 8) {
signbit_pos = 63;
exp_pos = 52;
exp_size = 11;
frac_pos = 0;
frac_size = 52;
bias = 1023;
jbitimplied = true;
}
maxexponent = (1<<exp_size)-1;
calcPrecision();
}
/// \param sign is set to \b true if the value should be negative
/// \param signif is the fractional part
/// \param exp is the exponent
/// \return the constructed floating-point value
double FloatFormat::createFloat(bool sign,uintb signif,int4 exp)
{
signif >>= 1; // Throw away 1 bit of precision we will
// lose anyway, to make sure highbit is 0
int4 precis = 8*sizeof(uintb) - 1; // fullword - 1 we threw away
double res = (double)signif;
int4 expchange = exp - precis + 1; // change in exponent is precis
// -1 integer bit
res = ldexp(res,expchange);
if (sign)
res = res * -1.0;
return res;
}
/// \brief Extract the sign, fractional, and exponent from a given floating-point value
///
/// \param x is the given value
/// \param sgn passes back the sign
/// \param signif passes back the fractional part
/// \param exp passes back the exponent
/// \return the floating-point class of the value
FloatFormat::floatclass FloatFormat::extractExpSig(double x,bool *sgn,uintb *signif,int4 *exp)
{
int4 e;
*sgn = signbit(x);
if (x == 0.0) return zero;
if (std::isinf(x)) return infinity;
if (std::isnan(x)) return nan;
if (*sgn)
x = -x;
double norm = frexp(x,&e); // norm is between 1/2 and 1
norm = ldexp(norm,8*sizeof(uintb)-1); // norm between 2^62 and 2^63
*signif = (uintb)norm; // Convert to normalized integer
*signif <<= 1;
e -= 1; // Consider normalization between 1 and 2
*exp = e;
return normalized;
}
/// \param x is an encoded floating-point value
/// \return the fraction part of the value aligned to the top of the word
uintb FloatFormat::extractFractionalCode(uintb x) const
{
x >>= frac_pos; // Eliminate bits below
x <<= 8*sizeof(uintb) - frac_size; // Align with top of word
return x;
}
/// \param x is an encoded floating-point value
/// \return the sign bit
bool FloatFormat::extractSign(uintb x) const
{
x >>= signbit_pos;
return ((x&1)!=0);
}
/// \param x is an encoded floating-point value
/// \return the (signed) exponent
int4 FloatFormat::extractExponentCode(uintb x) const
{
x >>= exp_pos;
uintb mask = 1;
mask = (mask<<exp_size) - 1;
return (int4)(x & mask);
}
/// \param x is an encoded value (with fraction part set to zero)
/// \param code is the new fractional value to set
/// \return the encoded value with the fractional filled in
uintb FloatFormat::setFractionalCode(uintb x,uintb code) const
{
// Align with bottom of word, also drops bits of precision
// we don't have room for
code >>= 8*sizeof(uintb) - frac_size;
code <<= frac_pos; // Move bits into position;
x |= code;
return x;
}
/// \param x is an encoded value (with sign set to zero)
/// \param sign is the sign bit to set
/// \return the encoded value with the sign bit set
uintb FloatFormat::setSign(uintb x,bool sign) const
{
if (!sign) return x; // Assume bit is already zero
uintb mask = 1;
mask <<= signbit_pos;
x |= mask; // Stick in the bit
return x;
}
/// \param x is an encoded value (with exponent set to zero)
/// \param code is the exponent to set
/// \return the encoded value with the new exponent
uintb FloatFormat::setExponentCode(uintb x,uintb code) const
{
code <<= exp_pos; // Move bits into position
x |= code;
return x;
}
/// \param sgn is set to \b true for negative zero, \b false for positive
/// \return the encoded zero
uintb FloatFormat::getZeroEncoding(bool sgn) const
{
uintb res = 0;
// Use IEEE 754 standard for zero encoding
res = setFractionalCode(res,0);
res = setExponentCode(res,0);
return setSign(res,sgn);
}
/// \param sgn is set to \b true for negative infinity, \b false for positive
/// \return the encoded infinity
uintb FloatFormat::getInfinityEncoding(bool sgn) const
{
uintb res = 0;
// Use IEEE 754 standard for infinity encoding
res = setFractionalCode(res,0);
res = setExponentCode(res,(uintb)maxexponent);
return setSign(res,sgn);
}
/// \param sgn is set to \b true for negative NaN, \b false for positive
/// \return the encoded NaN
uintb FloatFormat::getNaNEncoding(bool sgn) const
{
uintb res = 0;
// Use IEEE 754 standard for NaN encoding
uintb mask = 1;
mask <<= 8*sizeof(uintb)-1; // Create "quiet" NaN
res = setFractionalCode(res,mask);
res = setExponentCode(res,(uintb)maxexponent);
return setSign(res,sgn);
}
void FloatFormat::calcPrecision(void)
{
decimalMinPrecision = (int4)floor(frac_size * 0.30103);
// Precision needed to guarantee IEEE 754 binary -> decimal -> binary round trip conversion
decimalMaxPrecision = (int4)ceil((frac_size + 1) * 0.30103) + 1;
}
/// \param encoding is the encoding value
/// \param type points to the floating-point class, which is passed back
/// \return the equivalent double value
double FloatFormat::getHostFloat(uintb encoding,floatclass *type) const
{
bool sgn = extractSign(encoding);
uintb frac = extractFractionalCode(encoding);
int4 exp = extractExponentCode(encoding);
bool normal = true;
if (exp == 0) {
if ( frac == 0 ) { // Floating point zero
*type = zero;
return sgn ? -0.0 : +0.0;
}
*type = denormalized;
// Number is denormalized
normal = false;
}
else if (exp == maxexponent) {
if ( frac == 0 ) { // Floating point infinity
*type = infinity;
double infinity = std::numeric_limits<double>::infinity();
return sgn ? -infinity : +infinity;
}
*type = nan;
// encoding is "Not a Number" NaN
double nan = std::numeric_limits<double>::quiet_NaN();
return sgn ? -nan : +nan; // Sign is usually ignored
}
else
*type = normalized;
// Get "true" exponent and fractional
exp -= bias;
if (normal && jbitimplied) {
frac >>= 1; // Make room for 1 jbit
uintb highbit = 1;
highbit <<= 8*sizeof(uintb)-1;
frac |= highbit; // Stick bit in at top
}
return createFloat(sgn,frac,exp);
}
/// \brief Round a floating point value to the nearest even
///
/// \param signif the significant bits of a floating point value
/// \param lowbitpos the position in signif of the floating point
/// \return true if we rounded up
bool FloatFormat::roundToNearestEven(uintb &signif, int4 lowbitpos)
{
uintb lowbitmask = (lowbitpos < 8 * sizeof(uintb)) ? ((uintb)1 << lowbitpos) : 0;
uintb midbitmask = (uintb)1 << (lowbitpos - 1);
uintb epsmask = midbitmask - 1;
bool odd = (signif & lowbitmask) != 0;
if ((signif & midbitmask) != 0 && ((signif & epsmask) != 0 || odd)) {
signif += midbitmask;
return true;
}
return false;
}
/// \param host is the double value to convert
/// \return the equivalent encoded value
uintb FloatFormat::getEncoding(double host) const
{
floatclass type;
bool sgn;
uintb signif;
int4 exp;
type = extractExpSig(host, &sgn, &signif, &exp);
if (type == zero)
return getZeroEncoding(sgn);
else if (type == infinity)
return getInfinityEncoding(sgn);
else if (type == nan)
return getNaNEncoding(sgn);
// convert exponent and fractional to their encodings
exp += bias;
if (exp < -frac_size) // Exponent is too small to represent
return getZeroEncoding(sgn); // TODO handle round to non-zero
if (exp < 1) { // Must be denormalized
if (roundToNearestEven(signif, 8 * sizeof(uintb) - frac_size - exp)) {
// TODO handle round to normal case
if ((signif >> (8 * sizeof(uintb) - 1)) == 0) {
signif = (uintb)1 << (8 * sizeof(uintb) - 1);
exp += 1;
}
}
uintb res = getZeroEncoding(sgn);
return setFractionalCode(res, signif >> (-exp));
}
if (roundToNearestEven(signif, 8 * sizeof(uintb) - frac_size - 1)) {
// if high bit is clear, then the add overflowed. Increase exp and set
// signif to 1.
if ((signif >> (8 * sizeof(uintb) - 1)) == 0) {
signif = (uintb)1 << (8 * sizeof(uintb) - 1);
exp += 1;
}
}
if (exp >= maxexponent) // Exponent is too big to represent
return getInfinityEncoding(sgn);
if (jbitimplied && (exp != 0))
signif <<= 1; // Cut off top bit (which should be 1)
uintb res = 0;
res = setFractionalCode(res, signif);
res = setExponentCode(res, (uintb)exp);
return setSign(res, sgn);
}
/// \param encoding is the value in the \e other FloatFormat
/// \param formin is the \e other FloatFormat
/// \return the equivalent value in \b this FloatFormat
uintb FloatFormat::convertEncoding(uintb encoding,
const FloatFormat *formin) const
{
bool sgn = formin->extractSign(encoding);
uintb signif = formin->extractFractionalCode(encoding);
int4 exp = formin->extractExponentCode(encoding);
if (exp == formin->maxexponent) { // NaN or INFINITY encoding
exp = maxexponent;
if (signif != 0)
return getNaNEncoding(sgn);
else
return getInfinityEncoding(sgn);
}
if (exp == 0) { // incoming is subnormal
if (signif == 0)
return getZeroEncoding(sgn);
// normalize
int4 lz = count_leading_zeros(signif);
signif <<= lz;
exp = -formin->bias - lz;
}
else { // incoming is normal
exp -= formin->bias;
if (jbitimplied)
signif = ((uintb)1 << (8 * sizeof(uintb) - 1)) | (signif >> 1);
}
exp += bias;
if (exp < -frac_size) // Exponent is too small to represent
return getZeroEncoding(sgn); // TODO handle round to non-zero
if (exp < 1) { // Must be denormalized
if (roundToNearestEven(signif, 8 * sizeof(uintb) - frac_size - exp)) {
// TODO handle carry to normal case
if ((signif >> (8 * sizeof(uintb) - 1)) == 0) {
signif = (uintb)1 << (8 * sizeof(uintb) - 1);
exp += 1;
}
}
uintb res = getZeroEncoding(sgn);
return setFractionalCode(res, signif >> (-exp));
}
if (roundToNearestEven(signif, 8 * sizeof(uintb) - frac_size - 1)) {
// if high bit is clear, then the add overflowed. Increase exp and set
// signif to 1.
if ((signif >> (8 * sizeof(uintb) - 1)) == 0) {
signif = (uintb)1 << (8 * sizeof(uintb) - 1);
exp += 1;
}
}
if (exp >= maxexponent) // Exponent is too big to represent
return getInfinityEncoding(sgn);
if (jbitimplied && (exp != 0))
signif <<= 1; // Cut off top bit (which should be 1)
uintb res = 0;
res = setFractionalCode(res, signif);
res = setExponentCode(res, (uintb)exp);
return setSign(res, sgn);
}
/// The string should be printed with the minimum number of digits to uniquely specify the underlying
/// binary value. This currently only works for the 32-bit and 64-bit IEEE 754 formats.
/// If the \b forcesci parameter is \b true, the string will always be printed using scientific notation.
/// \param host is the given value already converted to the host's \b double format.
/// \param forcesci is \b true if the value should be printed in scientific notation.
/// \return the decimal representation as a string
string FloatFormat::printDecimal(double host,bool forcesci) const
{
string res;
for(int4 prec=decimalMinPrecision;;++prec) {
ostringstream s;
if (forcesci) {
s.setf( ios::scientific ); // Set to scientific notation
s.precision(prec-1); // scientific doesn't include first digit in precision count
}
else {
s.unsetf( ios::floatfield ); // Use "default" notation to allow fewer digits to be printed if possible
s.precision(prec);
}
s << host;
if (prec == decimalMaxPrecision) {
return s.str();
}
res = s.str();
double roundtrip = 0.0;
istringstream t(res);
if (size <= 4) {
float tmp = 0.0;
t >> tmp;
roundtrip = tmp;
}
else {
t >> roundtrip;
}
if (roundtrip == host)
break;
}
return res;
}
// Currently we emulate floating point operations on the target
// By converting the encoding to the host's encoding and then
// performing the operation using the host's floating point unit
// then the host's encoding is converted back to the targets encoding
/// \param a is the first floating-point value
/// \param b is the second floating-point value
/// \return \b true if (a == b)
uintb FloatFormat::opEqual(uintb a,uintb b) const
{
floatclass type;
double val1 = getHostFloat(a,&type);
double val2 = getHostFloat(b,&type);
uintb res = (val1 == val2) ? 1 : 0;
return res;
}
/// \param a is the first floating-point value
/// \param b is the second floating-point value
/// \return \b true if (a != b)
uintb FloatFormat::opNotEqual(uintb a,uintb b) const
{
floatclass type;
double val1 = getHostFloat(a,&type);
double val2 = getHostFloat(b,&type);
uintb res = (val1 != val2) ? 1 : 0;
return res;
}
/// \param a is the first floating-point value
/// \param b is the second floating-point value
/// \return \b true if (a < b)
uintb FloatFormat::opLess(uintb a,uintb b) const
{
floatclass type;
double val1 = getHostFloat(a,&type);
double val2 = getHostFloat(b,&type);
uintb res = (val1 < val2) ? 1 : 0;
return res;
}
/// \param a is the first floating-point value
/// \param b is the second floating-point value
/// \return \b true if (a <= b)
uintb FloatFormat::opLessEqual(uintb a,uintb b) const
{
floatclass type;
double val1 = getHostFloat(a,&type);
double val2 = getHostFloat(b,&type);
uintb res = (val1 <= val2) ? 1 : 0;
return res;
}
/// \param a is an encoded floating-point value
/// \return \b true if a is Not-a-Number
uintb FloatFormat::opNan(uintb a) const
{
floatclass type;
getHostFloat(a,&type);
uintb res = (type == FloatFormat::nan) ? 1 : 0;
return res;
}
/// \param a is the first floating-point value
/// \param b is the second floating-point value
/// \return a + b
uintb FloatFormat::opAdd(uintb a,uintb b) const
{
floatclass type;
double val1 = getHostFloat(a,&type);
double val2 = getHostFloat(b,&type);
return getEncoding(val1 + val2);
}
/// \param a is the first floating-point value
/// \param b is the second floating-point value
/// \return a / b
uintb FloatFormat::opDiv(uintb a,uintb b) const
{
floatclass type;
double val1 = getHostFloat(a,&type);
double val2 = getHostFloat(b,&type);
return getEncoding(val1 / val2);
}
/// \param a is the first floating-point value
/// \param b is the second floating-point value
/// \return a * b
uintb FloatFormat::opMult(uintb a,uintb b) const
{
floatclass type;
double val1 = getHostFloat(a,&type);
double val2 = getHostFloat(b,&type);
return getEncoding(val1 * val2);
}
/// \param a is the first floating-point value
/// \param b is the second floating-point value
/// \return a - b
uintb FloatFormat::opSub(uintb a,uintb b) const
{
floatclass type;
double val1 = getHostFloat(a,&type);
double val2 = getHostFloat(b,&type);
return getEncoding(val1 - val2);
}
/// \param a is an encoded floating-point value
/// \return -a
uintb FloatFormat::opNeg(uintb a) const
{
floatclass type;
double val = getHostFloat(a,&type);
return getEncoding(-val);
}
/// \param a is an encoded floating-point value
/// \return abs(a)
uintb FloatFormat::opAbs(uintb a) const
{
floatclass type;
double val = getHostFloat(a,&type);
return getEncoding(fabs(val));
}
/// \param a is an encoded floating-point value
/// \return sqrt(a)
uintb FloatFormat::opSqrt(uintb a) const
{
floatclass type;
double val = getHostFloat(a,&type);
return getEncoding(sqrt(val));
}
/// \param a is a signed integer value
/// \param sizein is the number of bytes in the integer encoding
/// \return a converted to an encoded floating-point value
uintb FloatFormat::opInt2Float(uintb a,int4 sizein) const
{
intb ival = sign_extend(a,8*sizein-1);
double val = (double) ival; // Convert integer to float
return getEncoding(val);
}
/// \param a is an encoded floating-point value
/// \param outformat is the desired output FloatFormat
/// \return a converted to the output FloatFormat
uintb FloatFormat::opFloat2Float(uintb a,const FloatFormat &outformat) const
{
return outformat.convertEncoding(a, this);
}
/// \param a is an encoded floating-point value
/// \param sizeout is the desired encoding size of the output
/// \return an integer encoding of a
uintb FloatFormat::opTrunc(uintb a,int4 sizeout) const
{
floatclass type;
double val = getHostFloat(a,&type);
intb ival = (intb) val; // Convert to integer
uintb res = (uintb) ival; // Convert to unsigned
res &= calc_mask(sizeout); // Truncate to proper size
return res;
}
/// \param a is an encoded floating-point value
/// \return ceil(a)
uintb FloatFormat::opCeil(uintb a) const
{
floatclass type;
double val = getHostFloat(a,&type);
return getEncoding(ceil(val));
}
/// \param a is an encoded floating-point value
/// \return floor(a)
uintb FloatFormat::opFloor(uintb a) const
{
floatclass type;
double val = getHostFloat(a,&type);
return getEncoding(floor(val));
}
/// \param a is an encoded floating-point value
/// \return round(a)
uintb FloatFormat::opRound(uintb a) const
{
floatclass type;
double val = getHostFloat(a,&type);
// return getEncoding(floor(val+.5)); // round half up
return getEncoding(round(val)); // round half away from zero
}
} // End namespace ghidra