在不使用modf()的情况下获取float的小数部分

时间:2010-04-07 18:27:33

标签: c++ c math bit-manipulation

我正在为没有数学库的平台开发,所以我需要构建自己的工具。我目前获得分数的方法是将浮点数转换为固定点(乘以(浮点)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;
}

提前致谢!

10 个答案:

答案 0 :(得分:43)

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

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

f = f-(long)f;

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

0.14159 = 3.14159 - 3;

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

答案 1 :(得分:7)

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

答案 2 :(得分:4)

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

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

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

定义了一些宏here

答案 3 :(得分:4)

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

答案 4 :(得分:2)

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

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

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

答案 5 :(得分:1)

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

Bresenham's line drawing

答案 6 :(得分:1)

好像你想要这个。

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

答案 7 :(得分:0)

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

要将其放入公式中,您的方法的工作原理是将(x modf 1.0)计算为((x << 16) mod 1<<16) >> 16,并且硬编码16应该取决于指数 - 确切的替换取决于您的浮动格式。

答案 8 :(得分:0)

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

答案 9 :(得分:0)

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