C 語言預設會幫我們作一些型別轉換的動作, 一般的型別轉換不只會更換資料型別, 更會使資料內容有所變動, 例如:

	float   fVar = 1.2;
	int32_t iVar;

	iVar = fVar;	// iVar 值為整數 1

但是萬一我們不想要這個預設的換轉, 只是想要把 fVar 的數值 (以 float 格式) 存放在變數 iVar 所佔用的記憶體中, 而又不想動用 union 設定呢? (因為使用 union 程式或多或少都需要修改, 萬一 compiler 不支援 Unnamed Structure and Union Fields (或者 Anonymous unions, C11 的功能), 則需要更動的幅度就會更大.)

答案是: (取值)<-(指標轉型)<-(取址)

	// 寫在等號右手邊
	iVar = *(int32_t*)&fVar;
	// 或者, 寫在等號左手邊
	*(float*)&iVar = fVar;

或者是反過來, 想要把 iVar 的數值 (以int32_t 格式) 存放在變數 fVar 所佔用的記憶體中.

	// 寫在等號右手邊
	fVar = *(float*)&iVar;
	// 或者, 寫在等號左手邊
	*(int32_t*)&fVar = iVar;

(取值)<-(指標轉型)<-(取址) 看似很複雜, 其實只是告訴 compiler: 我要用 (新指定的方式) 存取原本變數所在的位址, 所以看似很複雜的 C 程式碼, 但是編譯出來的目的碼並不會膨脹, 也沒有轉來轉去的問題.

Compiler 不支援 Anonymous unions 時改用 union 程式無論是使用整數型態存取, 或者使用浮點數型態存取的部份都需要修改, 寫法如下:

	float fVar = 1.2;
	union {
		int32_t i;
		float   f;
	} iVar;

	iVar.f = fVar;	// 浮點數型態的要改
	iVar.i = 123;	// 整數型態的也要改

Compiler 有支援 Anonymous unions 時, 只需要更動使用浮點數型態存取的部份, 寫法如下:

	float fVar = 1.2;
	union {
		int32_t iVar;
		float   iVarf;
	};

	iVarf = fVar;	// 浮點數型態的要改
	iVar  = 123;	// 整數型態的不用改

應用實例: 平方根倒數快速演算法


此即讀者 ">w0" 留言所說的那個演算法:

float Q_rsqrt( float number )
{
	long i;
	float x2, y;
	const float threehalfs = 1.5F;

	x2 = number * 0.5F;
	y  = number;
	i  = * ( long * ) &y;						// evil floating point bit level hacking
	i  = 0x5f3759df - ( i >> 1 );				// what the fuck?
	y  = * ( float * ) &i;
	y  = y * ( threehalfs - ( x2 * y * y ) );	// 1st iteration
//	y  = y * ( threehalfs - ( x2 * y * y ) );	// 2nd iteration, this can be removed

	return y;
}

第 9 行即是最前面提到的 (取值).

這裡我不多解釋這個函數, 有興趣的讀者可以參閱:

相關文章


arrow
arrow
    創作者介紹
    創作者 MagicJackTing 的頭像
    MagicJackTing

    傑克! 真是太神奇了!

    MagicJackTing 發表在 痞客邦 留言(1) 人氣()