簡體   English   中英

如何在 C++ 中將浮點數轉換為定點(更高精度)

[英]How to convert float to fixed point (higher precision) in C++

我正在嘗試在 C++ 中實現我自己的定點算術以(稍后)進行更高精度的計算。 我在想像

class FixedPoint
{
int intPart;
unsigned long long fracPart[some number];
}

我認為如果我 - 例如加法 - 首先添加兩個 fracPart[some number],如果它們溢出,則將 1 添加到 fracPart[some number - 1] 等等。

但是我堅持將雙“d”轉換為這樣的類。 intPart = d 當然有效。 然后做

double Temp = d - intPart;

給我小數部分。 但是我如何正確地將它分配給 fracPart[0]? 在十進制中,如果 long long 正好有 20 位,我可以只做 Temp * 100000000000000000000,這樣 0.14 就變成 14000000000000000000。但是如果在二進制中,我取 d 的尾數位(53/504 位)frac[Part[53/54] ](64位),添加隱藏位並將其左移13(或12,因為隱藏位),該值是錯誤的。 到目前為止,我在網上找到的任何東西都沒有幫助...

忘記十進制。 使用 2 的冪。您的第一個小數部分應包含值為 2^-1、2^-2、... 2^-64 的位。 浮點數的好處是您可以輕松地按 2 的冪縮放您的值。 換句話說,減去整數部分,然后乘以 2^64,然后取下一個整數部分,依此類推。 這樣的事情應該適合你:

#include <cmath>
// using std::floor, std::ldexp
#include <cstdint>
// using std::int64_t, std::uint64_t
#include <cstdio>
// using std::printf


class FixedPoint
{
  std::int64_t ipart;
  std::uint64_t fpart[2];

public:
  explicit FixedPoint(double f) noexcept
  {
    // rounded down so that the fractional part is always positive
    ipart = std::floor(f);
    f -= ipart;
    for(std::uint64_t& fractional: fpart) {
      f = std::ldexp(f, 64);
      fractional = f;
      f -= fractional;
    }
  }
  operator double() const noexcept
  {
    double f = 0.;
    for(int i = 1; i >= 0; --i) {
      f += fpart[i];
      f = std::ldexp(f, -64);
    }
    f += ipart;
    return f;
  }
};



int main()
{
  double f1 = 123.4567;
  FixedPoint p(f1);
  double f2 = p;
  std::printf("%g = %g\n", f1, f2);
}

最后的一些想法:

  1. 我希望你知道有實際的圖書館可以為你做這種事情嗎? 我認為這只是一個練習,用浮點和定點弄濕你的腳。 否則停止並停止。 ;-)
  2. 我切換到 std::uint64_t 是因為在數據類型中使用標准精度更方便。
  3. 使用 uint64_t 的一個缺點是 x86_64 中沒有快速 double <-> uint64_t 機器指令。 使用 uint32_t 實際上可能更快。
  4. 對整數部分使用 int 而對小數部分使用更大的類型是沒有意義的。 由於對齊,您只會在結構中浪費 32 位空間,您可以將這些空間用於更大的范圍。 要么切換到 32 位,要么使用 64 位作為整數部分,並保持小數部分的大小(整個數組的大小)為 64 位的倍數,例如 2 x 32 位
  5. 請注意, std::frexp為您提供浮點數的指數和范圍 [0.5, 1) (或零)內的歸一化尾數。 這將允許您在不損失精度的情況下用任意大范圍的指數替換整數部分。 當然,此時您只是在軟件中重新實現擴展精度浮點數。

你應該

#include <boost/multiprecision.hpp>

對真的。 實現你自己的 bignum 可能很有趣也很有啟發性,但你會搞砸。 在你從未想過的事情上。 有很多小的邊緣情況和你需要做的事情 - 特別是在做非整數 bignums 時。

Boost 庫是正確的,並且使它在您自己的代碼中非常容易使用。

(假設您不想包含整個 MP 庫,您實際上可以僅包含您希望使用的 MP 類型的更具體的文件。)

順便說一句,定點是一個整數,由某個固定因子縮放。 例如,假設您正在為銀行業編寫軟件。 您可以將值存儲為百分之一,一個(美元 * 10000)因子。 你要做的不是固定點。

在您的情況下,使用字符串是否是有效的解決方案? 您可以重載所有數學運算並像使用紙筆一樣實現它們。 這是一項大量工作,您可能會遇到非平凡的效率問題,但它可能是一個可行的解決方案。 我不清楚您是否在將 double 轉換為結構時遇到問題,但這是一個使用字符串的最小工作示例:

#include <cmath>
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>

int main()
{
    int prec=20;
    double a=1.3235335151561;
    int intpart=std::floor(a);
    double tmp=a-intpart;
    std::ostringstream os;
    os<<std::setprecision(prec)<<tmp;
    std::string str = os.str();
    std::cout<<str<<std::endl;
    int frac_part[prec];
    for(int i=0; i<prec; ++i){
        frac_part[i]=(int)str[i+2]-48;
        //jumps 0., and the ascii number for '0' is 48
        std::cout<<frac_part[i]<<" ";
    }
    std::cout<<"\n";

    }

當然,一旦你有使用小數部分的數組的字符串在我看來是多余的。

暫無
暫無

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

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