C++ 不使用modf()获取浮点的小数部分

C++ 不使用modf()获取浮点的小数部分,c++,c,math,bit-manipulation,C++,C,Math,Bit Manipulation,我正在开发一个没有数学库的平台,所以我需要构建自己的工具。我目前获取分数的方法是将浮点转换为定点(与(float)0xFFFF相乘,转换为int),仅获取较低部分(与0xFFFF掩码),然后再次将其转换回浮点 然而,这种不精确性正折磨着我。我正在使用Frac()和InvFrac()函数绘制抗锯齿线。使用modf我得到了一条非常平滑的线条。用我自己的方法,由于精度损失,像素开始跳跃 这是我的代码: const float fp_amount = (float)(0xFFFF); const flo

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

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

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

(出于法律原因,它似乎是BSD授权的,但您显然希望再次检查)


有些宏是定义的。

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

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

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


有关32位浮点的存储机制的描述,请参阅

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


要将其放入公式中,您的方法通过计算
(x modf 1.0)
作为
((x常数中有一个错误。你基本上是尝试将数字左移16位,屏蔽掉除低位以外的所有内容,然后再右移16位。移位与乘以2的幂相同,但你没有使用2的幂-你使用的是0xFFFF,它被关1。用0x10000替换它将导致让公式按预期工作。

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


看起来你可能想要这个

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

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


简单且适用于-ve和+ve

为什么所有的位移位真的有那么大的速度增益?或者我的小小的整数转换技巧有一些我不知道的问题吗?@Daniel Bingham:可能是后者。在你使用的平台上,浮点数可能没有按照你的想法编码,所以你的掩码可能会关闭。@sharth:你的链接依赖于一些宏,我很难找到它们。你能试试运气,找到提取单词、插入单词和获取高单词的定义吗?Int到float很好。float到Int的速度非常慢。一个if…然后…else…在一个像这样经常使用的数学函数中?我的缓存,它会流泪!当
f
为负数时,这是错误的。(您正在添加两个负数。)您根本不需要
if
f=f-(int)f
。如果
f
是负数,它将减去一个四舍五入为零的负整数。另外,你假设浮点的整数部分适合整数。@jamesdlin谢谢,我用负数部分来做了心理准备。对了,你是关于浮点/整数的事情。哦,好吧。这个答案是最容易实现的,也是最简单的最快的(比我的方法快20%左右).接受!fp_金额不应该是0x10000而不是0xFFFF吗?天哪!做一个答案,这样我就可以接受!你刚刚解决了我的整个准确性问题!如果评论修正了,为什么没有人编辑原始代码?
floor
也比cast慢。对于反锯齿行,看到太糟糕了,原始海报没有编辑他的答案,如果这样的话事实上,s确实解决了这个问题。
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