web-dev-qa-db-ja.com

modf()を使用せずにフロートの小数部を取得する

数学ライブラリのないプラットフォーム用に開発しているので、独自のツールを構築する必要があります。現在の分数を取得する方法は、フロートを固定小数点に変換し((float)0xFFFFを乗算し、intにキャスト)、下部のみを取得し(0xFFFFを使用してマスク)、再びフロートに変換します。

しかし、その不正確さが私を殺しています。 Frac()およびInvFrac()関数を使用して、アンチエイリアス処理された線を描画しています。 modfを使用すると、完全に滑らかな線になります。私自身の方法では、精度の低下によりピクセルが動き回ります。

これは私のコードです:

const float fp_amount = (float)(0xFFFF);
const float fp_amount_inv = 1.f / fp_amount;

inline float Frac(float a_X)
{
    return ((int)(a_X * fp_amount) & 0xFFFF) * fp_amount_inv;
}

inline float Frac(float a_X)
{
    return (0xFFFF - (int)(a_X * fp_amount) & 0xFFFF) * fp_amount_inv;
}

前もって感謝します!

25
knight666

私があなたの質問を正しく理解していれば、小数点以下の部分だけが欲しいですか?実際には分数(整数分子と分母)は必要ありませんか?

つまり、3.14159と言って、0.14159だけにしてください。番号がfloat f;に格納されていると仮定すると、次のようになります。

f = f-(long)f;

これは、番号を挿入すると、次のように機能します。

0.14159 = 3.14159 - 3;

これにより、浮動小数点数の整数部分が削除され、小数部分のみが残ります。 floatをlongに変換すると、小数点以下が切り捨てられます。次に、元のフロートからそれを引くと、小数点以下のonlyが残ります。 floatタイプ(ほとんどのシステムでは8バイト)のサイズのため、ここではlongを使用する必要があります。整数(多くのシステムでは4バイトのみ)は、floatと同じ範囲の数値をカバーするのに十分な大きさである必要はありませんが、longはそうである必要があります。

44
Daniel Bingham

私が思ったように、modfは算術自体を使用していません-すべてのシフトとマスクです、見てください ここ 。プラットフォームで同じアイデアを使用できませんか?

8
AVB

今日使用しているシステムでmodfがどのように実装されているかを確認することをお勧めします。 uClibcのバージョンを確認してください。

http://git.uclibc.org/uClibc/tree/libm/s_modf.c

(法的な理由により、BSDライセンスであるように見えますが、明らかに再確認する必要があります)

一部のマクロは定義されています here

5
Bill Lynch

定数にバグがあります。基本的に、数値を16ビット左シフトし、下位ビット以外のすべてをマスクしてから、再び16ビット右シフトしようとしています。シフトは2の累乗を乗算することと同じですが、2の累乗を使用していません。0から1だけ離れた0xFFFFを使用しています。これを0x10000に置き換えると、式が意図したとおりに機能します。

4
Mark Ransom

完全にはわかりませんが、仮数だけを考慮して指数を完全に忘れているだけなので、あなたがしていることは間違っていると思います。

実際の整数部分を見つけるには、指数を使用して仮数の値をシフトする必要があります。

32ビット浮動小数点の格納メカニズムの説明については、 ここ を参照してください。

3
Bruno Brant

多分あなたはこれが欲しいようです。

float f = something;
float fractionalPart = f - floor(f);
1
Victor Engel

なぜ線画のために浮動小数点に行くのですか?固定小数点バージョンに固執し、代わりに整数/固定小数点ベースの線描画ルーチンを使用できます-Bresenhamが思い浮かびます。このバージョンにはエイリアスがありませんが、他にもエイリアスがあることは知っています。

ブレゼンハムの線画

1
Michael Dorgan

あなたの方法は、小数部に16ビットがあることを前提としています(Mark Ransomが注記しているように、16ビットでシフトする必要があります(つまり、0x1000で乗算します)。それは本当ではないかもしれません。指数は、小数部分にあるビット数を決定するものです。

これを数式に入れるには、メソッドは(x modf 1.0) なので ((x << 16) mod 1<<16) >> 16、そして指数に依存するはずのハードコードされた16です-正確な置換はフロート形式に依存します。

0
MSalters

1つのオプションは、fmod(x, 1)を使用することです。

0
emlai
double frac(double val)
{
    return val - trunc(val);
}

// frac(1.0) = 1.0 - 1.0 = 0.0 correct
// frac(-1.0) = -1.0 - -1.0 = 0.0 correct
// frac(1.4) = 1.4 - 1.0 = 0.4 correct
// frac(-1.4) = -1.4 - -1.0 = -0.4 correct

シンプルで、-veおよび+ veで機能します

0
TheWhitde