編程之美 - 浮點數的精確表示

問題描述:
如何將浮點數(小數)轉換爲分數的形式,這樣可以更精確的表達出浮點數的值。

問題分析:
小數可以分爲兩種,有限小數和無限循環小數。

有限小數的處理相對簡單:
只要把它乘以10的N次方,轉換爲整數,再和10的N次方都除以它們的最大公約數即可。
例如:0.25  把它乘以 10的2次方 變成25, 25/100 分子分母除以最大公約數25變成1/4即可。

無限循環小數的處理要複雜一些:
書中使用了一個有趣的計算方法,它把無限循環小數分爲一小數部分和一個循環部分 例如: X=0.a1a2...am(b1b2...bn)
這裏a是小數部分,b是循環部分。
1)  X先乘以10的m次方,把小數部分變爲整數   X*10^m ==> a1a2...am + 0.b1b2...bn(b1b2...bn)
     X = (a1a2...am + 0.b1b2...bn(b1b2...bn))/(10^m)

2)  假設 Y = 0.b1b2...bn(b1b2...bn),那麼Y可以轉換爲 Y*10^n = b1b2...bn + 0.b1b2...bn(b1b2...bn)
      Y*10^n = b1b2...bn + Y   ==>   Y =  b1b2...bn/(10^n - 1)

3)  把Y的部分代入X的等式中  X = (a1a2...am + b1b2...bn/(10^n - 1))/(10^m)
     X = (a1a2...am * (10^n - 1) + b1b2...bn) / ((10^n - 1)*10^m)

例如 0.3(3) = (3*9 + 3) / (9*10) = 30 / 90 = 1/3


實例程序:
程序中的缺陷,如果循環小數位數太多,會造成乘積超過int的範圍,造成計算錯誤。

#include <iostream>

using namespace std;

typedef struct _data
{
    unsigned long _a;
    unsigned long _b;   // the loop part, if = 0 means no
} data_t;

unsigned long gcb(unsigned long a, unsigned long b)
{
    unsigned long temp = 0, r = 0;

    // make sure a < b
    if (a > b)
    {
        temp = a;
        a = b;
        b = temp;
    }

    r = b % a;
    while(r != 0)
    {
        b = a;
        a = r;
        r = b%a;
    }

    return a;
}

void calc(data_t data)
{
    unsigned long temp = 0, gcb_val = 0;
    unsigned long power_a = 1, power_b = 1;

    temp = data._a;
    while (temp != 0)
    {
        temp = temp/10;
        power_a *= 10;
    }

    temp = data._b;
    while (temp != 0)
    {
        temp = temp/10;
        power_b *= 10;
    }

    cout << "a->" << data._a << ":" << power_a << "   b->" <<data._b << ":" << power_b << endl;

    // no loop part
    if (data._b == 0)
    {
        gcb_val = gcb(data._a, power_a);
        cout << data._a/gcb_val << "/" << power_a/gcb_val << endl;
    }
    else
    {
        temp = (data._a * (power_b - 1)) + data._b;
        power_b = power_a*(power_b - 1);
        gcb_val = gcb(temp, power_b);
        cout << temp/gcb_val << "/" << power_b/gcb_val << endl;
    }
}


int main()
{
    data_t test;
    int val = 0;
    //test._a = 1; test._b = 0;
    //test._a = 23; test._b = 0;
    //test._a = 3; test._b = 3;
    test._a = 1; test._b = 1;
    //test._a = 285714; test._b = 285714;   // error: int is overflow

    calc(test);

    cin >> val;
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章