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

问题描述 投票:0回答:10

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

提前致谢!

c++ c math bit-manipulation
10个回答
58
投票

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

所以我们有一些数字,比如

3.14159
,我们希望最终得到
0.14159
。假设我们的号码存储在
float f;
中,我们可以这样做:

f = f-(long)f;

如果我们插入我们的号码,工作原理如下:

0.14159 = 3.14159 - 3;

它的作用是删除浮点数的整数部分,只留下小数部分。当您将 float 转换为 long 时,它会删除小数部分。然后,当您从原始浮点数中减去该值时,就只剩下小数部分了。由于 float 类型的大小(在大多数系统上为 8 个字节),我们需要在此处使用 long。整数(在许多系统上只有 4 个字节)不一定大到足以覆盖与

float
相同的数字范围,但
long
应该足够大。
    


9
投票
modf

本身不使用任何算术

——都是移位和掩码,请看
这里。你不能在你的平台上使用同样的想法吗?

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

5
投票

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

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

一些宏定义于

here

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

5
投票

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

3
投票
您需要使用指数移动尾数中的值才能找到实际的整数部分。

有关 32 位浮点数存储机制的说明,请查看

这里

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

1
投票

布雷森汉姆的线条画

看起来也许你想要这个。

1
投票
float f = something; float fractionalPart = f - floor(f);

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

0
投票
要将其放入公式中,您的方法通过将

(x modf 1.0)

计算为

((x << 16) mod 1<<16) >> 16

 来工作,而硬编码的 16 应该取决于指数 - 确切的替换取决于您的浮点格式。

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

0
投票
简单,适用于 -ve 和 +ve
    

您可以使用

-2
投票

#include <math.h>

fmod(1.42, 1); // => 0.42
fmod(-1.42, 1); // => -0.42


© www.soinside.com 2019 - 2024. All rights reserved.