2010-01-25 25 views
4

在C++中,我有一個bigint類,它可以容納任意大小的整數。將float轉換爲bigint(又名便攜式獲取二進制指數和尾數)

我想將大浮點數或雙數轉換爲bigint。 我有一個工作方法,但它有點破解。我使用IEEE 754數字規範來獲取輸入數字的二進制符號,尾數和指數。

下面是代碼(註冊在這裏忽略不計,這並不重要):

float input = 77e12; 
bigint result; 

// extract sign, exponent and mantissa, 
// according to IEEE 754 single precision number format 
unsigned int *raw = reinterpret_cast<unsigned int *>(&input); 
unsigned int sign = *raw >> 31; 
unsigned int exponent = (*raw >> 23) & 0xFF; 
unsigned int mantissa = *raw & 0x7FFFFF; 

// the 24th bit is always 1. 
result = mantissa + 0x800000; 

// use the binary exponent to shift the result left or right 
int shift = (23 - exponent + 127); 
if (shift > 0) result >>= shift; else result <<= -shift; 

cout << input << " " << result << endl; 

它的工作原理,但它是相當醜陋,我不知道它是多麼便於攜帶。有一個更好的方法嗎?是否有一種不太醜陋,便攜的方式來從浮點數或雙精度中提取二進制尾數和指數?


感謝您的回答。爲了後代,這是一個使用frexp的解決方案。由於循環效率較低,但它適用於float和double類型,不使用reinterpret_cast或依賴任何有關浮點數表示的知識。

float input = 77e12; 
bigint result; 

int exponent; 
double fraction = frexp (input, &exponent); 
result = 0; 
exponent--; 
for (; exponent > 0; --exponent) 
{ 
    fraction *= 2; 
    if (fraction >= 1) 
    { 
     result += 1; 
     fraction -= 1; 
    } 
    result <<= 1; 
} 
+1

順便說一下,如果你使它成爲'unsigned int raw&= * reinterpret_cast (&input); ',你擺脫了所有其他的解除引用。 – GManNickG 2010-01-25 16:58:21

+0

該計劃的結果是7.699999752192e13,而不是7.7e13。 正如我在下面的答案中所說的,單行代碼 - 無符號long long float_to_int =(unsigned long long)input; - 給出與你的程序相同的答案。 – 2010-01-26 01:54:30

回答

8

難道你不能正常提取使用frexp(), frexpf(), frexpl()的值嗎?

+3

雖然在C++中最好使用'std :: frexp()',它被'float','double'和'long double'參數重載。 – 2010-01-25 17:01:36

+0

@Mike,好點! – 2010-01-25 17:06:00

+1

frexp()以float形式返回有效數(尾數),但OP使用它作爲整數。 – 2010-01-25 19:00:38

-1

如果float始終包含一個整數值,只需將其轉換爲int:float_to_int =(unsigned long)輸入。

BTW,77e12溢出一個浮點數。一個double會保存它,但是你需要這個cast:(unsigned long long)輸入。

+0

Ehm no ... 77e12不溢出浮子。指數可以從-126到127.鑄造到int正是我​​想要避免的,這就是爲什麼我使用bigint類。 – amarillion 2010-01-25 20:10:53

+0

「溢出」是錯誤的詞 - 對不起(這是否值得投票?)。 77e12需要47位來表示。這不能適應浮動 - 除非你想要它被截斷。你的編譯器不會給你一個警告嗎?我的確如此。 – 2010-01-25 22:03:58

+0

只是要清楚:你將一個77000000000000分配給一個浮點數,並且該浮點數取值爲76999997521920.這就是你想要的嗎? – 2010-01-25 22:15:49

1

我喜歡你的解決方案!這讓我走上了正軌。

雖然我會推薦一件事 - 爲什麼不一次得到一堆點,幾乎總是消除任何循環?我實現了一個float到BIGINT功能是這樣的:

template<typename F> 
explicit inline bigint(F f, typename std::enable_if<(std::is_floating_point<F>::value)>::type* enable = nullptr) { 
    int exp; 
    F fraction = frexp(fabs(f),&exp); 
    F chunk = floor(fraction *= float_pow_2<F,ulong_bit_count>::value); 
    *this = ulong(chunk); // will never overflow; frexp() is guaranteed < 1 
    exp -= ulong_bit_count; 
    while (sizeof(F) > sizeof(ulong) && (fraction -= chunk)) // this is very unlikely 
    { 
     chunk = floor(fraction *= float_pow_2<F,ulong_bit_count>::value); 
     *this <<= ulong_bit_count; 
     (*this).data[0] = ulong(chunk); 
     exp -= ulong_bit_count; 
    } 
    *this <<= exp; 
    sign = f < 0; 
} 

(順便說一句,我不知道一個簡單的方法把浮冪的兩點常量,所以我定義爲float_pow_2下面):

template<typename F, unsigned Exp, bool Overflow = (Exp >= sizeof(unsigned))> 
struct float_pow_2 { 
    static constexpr F value = 1u << Exp; 
}; 
template<typename F, unsigned Exp> 
struct float_pow_2<F,Exp,true> { 
    static constexpr F half = float_pow_2<F,Exp/2>::value; 
    static constexpr F value = half * half * (Exp & 1 ? 2 : 1); 
}; 
相關問題