簡體   English   中英

添加 IEEE-754 格式的正數和負數

[英]Adding positive and negative numbers in IEEE-754 format

我的問題似乎很簡單:我編寫了一個手動將浮點數相加的程序。 這個程序有一定的限制。 (例如沒有 iostream 或使用任何一元運算符),這就是缺少這些東西的原因。 至於問題,當添加兩個正浮點數(例如,1.5 + 1.5 = 3.0)時,程序似乎 function 正確,但是當添加兩個負數(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。如果指數為零,則它為零。 這應該在您的 Encode 和 Decode 函數中處理。 對於您的代碼的 rest,它只是一個有效位,不應有任何特殊處理。

當您開始考慮舍入時,您會發現在中間結果中通常需要超過 23 位。

將負數的有效位設為 2 的補碼將產生以兩種方式存儲相同信息的問題。 您將同時擁有一個符號位,就像在做符號和大小一樣,並且將符號編碼在有符號的 integer 符號中。 保持它們一致將是一團糟。 無論您決定 Real 將如何存儲負數,都將其記錄下來並始終保持一致。

如果我要實現這一點,我會從非常非常仔細地定義 Real 開始。 然后我會決定我希望能夠在 Real 上執行哪些操作,並編寫函數來執行這些操作。 如果你做對了,每個 function 都會相對簡單。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM