以IEEE-754格式添加正数和负数

时间:2020-07-26 19:58:25

标签: c++ floating-point ieee-754

我的问题似乎非常简单:我编写了一个程序,将手动将浮点数加在一起。该程序有一定的限制。 (例如,不使用iostream或使用任何一元运算符),这就是缺少这些内容的原因。至于问题,当添加两个正浮点数(例如1.5 + 1.5 = 3.0)时,该程序似乎可以正常运行,但是当添加两个负浮点数(10.0 + -5.0)时,我得到了非常古怪的数字。这是代码:

#include <cstdio>
#define BIAS32 127

struct Real
{
    //sign bit
    int sign;
    //UNBIASED exponent
    long exponent;
    //Fraction including implied 1. at bit index 23
    unsigned long fraction;
};

Real Decode(int float_value);
int Encode(Real real_value);
Real Normalize(Real value);
Real Add(Real left, Real right);
unsigned long Add(unsigned long leftop, unsigned long rightop);
unsigned long Multiply(unsigned long leftop, unsigned long rightop);
void alignExponents(Real* left, Real* right);
bool is_neg(Real real);
int Twos(int op);

int main(int argc, char* argv[])
{
    int left, right;
    char op;
    int value;
    Real rLeft, rRight, result;
    if (argc < 4) {
        printf("Usage: %s <left> <op> <right>\n", argv[0]);
        return -1;
    }
    sscanf(argv[1], "%f", (float*)&left);
    sscanf(argv[2], "%c", &op);
    sscanf(argv[3], "%f", (float*)&right);
    rLeft = Decode(left);
    rRight = Decode(right);

    if (op == '+') {
        result = Add(rLeft, rRight);
    }
    else {
        printf("Unknown operator '%c'\n", op);
        return -2;
    }
    value = Encode(result);
    printf("%.3f %c %.3f = %.3f (0x%08x)\n",
        *((float*)&left),
        op,
        *((float*)&right),
        *((float*)&value),
        value
    );
    return 0;
}

Real Decode(int float_value)
{             // Test sign bit of float_value - Test exponent bits of float_value & apply bias - Test mantissa bits of float_value
    Real result{ float_value >> 31 & 1 ? 1 : 0, ((long)Add(float_value >> 23 & 0xFF, -BIAS32)), (unsigned long)float_value & 0x7FFFFF };
    return result;
};
    
int Encode(Real real_value)
{
    int x = 0;
    x |= real_value.fraction; // Set the fraction bits of x 
    x |= real_value.sign << 31; // Set the sign bits of x
    x |= Add(real_value.exponent, BIAS32) << 23; // Set the exponent bits of x
    return x;
}

Real Normalize(Real value)
{
    if (is_neg(value))
    {
        value.fraction = Twos(value.fraction);
    }
    unsigned int i = 0;
    while (i < 9)
    {
        if ((value.fraction >> Add(23, i)) & 1) // If there are set bits past the mantissa section
        {
            value.fraction >>= 1; // shift mantissa right by 1
            value.exponent = Add(value.exponent, 1); // increment exponent to accomodate for shift
        }
        i = Add(i, 1);
    }
    return value;
}

Real Add(Real left, Real right)
{
    Real a = left, b = right;
    alignExponents(&a, &b); // Aligns exponents of both operands
    unsigned long sum = Add(a.fraction, b.fraction);
    Real result = Normalize({ a.sign, a.exponent, sum }); // Normalize result if need be
    return result;
}

unsigned long Add(unsigned long leftop, unsigned long rightop)
{
    unsigned long sum = 0, test = 1; // sum initialized to 0, test created to compare bits
    while (test) // while test is not 0
    {
        if (leftop & test) // if the digit being tested is 1
        {
            if (sum & test) sum ^= test << 1; // if the sum tests to 1, carry a bit over
            sum ^= test;
        }
        if (rightop & test)
        {
            if (sum & test) sum ^= test << 1;
            sum ^= test;
        }
        test <<= 1;
    }
    return sum;
}

void alignExponents(Real* a, Real* b)
{
    if (a->exponent != b->exponent) // If the exponents are not equal
    {
        if (a->exponent > b->exponent)
        {
            int disp = a->exponent - b->exponent; // number of shifts needed based on difference between two exponents
            b->fraction |= 1 << 23; // sets the implicit bit for shifting
            b->exponent = a->exponent; // sets exponents equal to each other
            b->fraction >>= disp; // mantissa is shifted over to accomodate for the increase in power
            return;
        }
        int disp = b->exponent - a->exponent;
        a->fraction |= 1 << 23;
        a->exponent = b->exponent;
        a->fraction >>= disp;
        return;
    }
    return;
}

bool is_neg(Real real)
{
    if (real.sign) return true;
    return false;
}

int Twos(int op)
{
    return Add(~op, -1); // NOT the operand and add 1 to it
}

最重要的是,我刚刚测试了值10.5 + 5.5并得到了24.0,所以似乎出现了比我最初想象的还要多的错误。我已经为此工作了好几天,希望能获得一些帮助/建议。

1 个答案:

答案 0 :(得分:0)

这里有一些帮助/建议。既然您已经处理了一些代码,我建议返回并重新处理您的数据结构。这样的关键数据结构的声明将受益于更多注释,确保您确切了解每个字段的含义。

例如,隐式位并不总是1。如果指数为零,则为零。这应该在您的编码和解码功能中处理。对于您的其余代码,这只是一个重要的部分,不应进行任何特殊处理。

当您开始考虑舍入时,您会发现中间结果中经常需要超过23位。

使负数2的补码有效,将产生一个问题,即以两种方式存储相同的信息。您将同时拥有一个符号位,就像在做符号和幅度一样,并且将符号编码为带符号的整数符号。保持它们一致将是一团糟。无论您决定Real如何存储负数,对其进行记录并在整个过程中保持一致。

如果要实现这一点,我将非常非常仔细地定义Real。然后,我将决定要在Real上执行哪些操作,并编写函数来执行这些操作。如果您做对了,每个功能都会相对简单。