在不使用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; } 

提前致谢!

如果我理解你的问题,你只想要小数点后的部分吗? 你实际上不需要它(整数分子和分母)?

所以我们有一些数字,比如3.14159 ,我们希望最终只有0.14159 。 假设我们的号码存储在float f; , 我们做得到:

 f = f-(long)f; 

如果我们插入我们的号码,其工作方式如下:

 0.14159 = 3.14159 - 3; 

这样做是删除浮点数的整数部分,只留下小数部分。 将float转换为long时,会丢弃小数部分。 然后当你从原始浮点数中减去它时,你只剩下小数部分。 我们需要在这里使用long,因为float类型的大小(在大多数系统上为8个字节)。 一个整数(在许多系统上只有4个字节)不一定足以覆盖与float相同的数字范围,但应该是long

正如我所怀疑的那样, modf本身不使用任何算术 – 它是所有的移位和掩码,请看这里 。 你不能在你的平台上使用相同的想法吗?

我建议你看看如何在你今天使用的系统上实现modf。 查看uClibc的版本。

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

(出于法律原因,它似乎是BSD许可的,但你显然想要仔细检查)

这里定义了一些宏。

你的常量中有一个错误。 你基本上试图将数字左移16位,屏蔽除了低位之外的所有内容,然后再向右移16位。 移位与乘以2的幂相同,但你没有使用2的幂 – 你使用的是0xFFFF,它被关闭1.用0x10000替换它将使公式按预期工作。

我不完全确定,但我认为你所做的是错的,因为你只是在考虑尾数而完全忘记了指数。

您需要使用指数来移动尾数中的值以查找实际的整数部分。

有关32位浮点存储机制的描述,请查看此处 。

为什么要为你的画线去浮点? 你可以坚持你的定点版本,并使用基于整数/定点的线条绘制程序 – Bresenham的想法。 虽然这个版本没有别名,但我知道还有其他版本。

Bresenham的画线

好像也许你想要这个。

 float f = something; float fractionalPart = f - floor(f); 

你的方法是假设小数部分有16位(正如Mark Ransom所说,这意味着你应该移位16位,即乘以0x1000)。 这可能不是真的。 指数决定了小数部分中有多少位。

把它放在一个公式中,你的方法通过计算(x modf 1.0)((x << 16) mod 1<<16) >> 16 ,并且它是硬编码16,它应该取决于指数 – 确切的替换取决于你的浮动格式。

一种选择是使用fmod(x, 1)