3

因为我的案例与游戏项目有关,所以我考虑在GameDev上发布这个问题,但我认为这更适合更广泛的编程 SE。请让我知道这个问题是否会更好地发布在那里。

根据我的理解——如果我错了,请纠正我——移动游戏开发(这是我的情况)可以从使用定点计算中受益匪浅,因为这将确保平台之间的更多一致性,并且会提高性能设备无法正确处理浮点的情况。

但是定点确实有局限性,最明显的是溢出。所以我的问题是,假设我们已经确定定点是给定目标平台的最佳选择,如何确定给定项目的要求是否允许使用它们。

为了让事情更清楚,我想分享一些我遇到问题的代码:

CIwVec2 calculateBezier(float t, CIwVec2 p0, CIwVec2 p1, CIwVec2 p2) {
    float u = 1 - t;

    CIwVec2 p = IW_FIXED(u * u) * p0;   // (1 - t)²P0
    p += IW_FIXED(2 * u * t) * p1;      // 2(1 - t)tP1
    p += IW_FIXED(t * t) * p2;          // t²P2

    return p;
}

在这个项目中,我使用Marmalade SDK,它使用 C++ 并带有自己的定点数实现(它们有 16 位和 32 位,我现在使用的是 32 位)和一个 Vector 类(CIwVec2) 使用该实现进行位置和计算(包括标量乘法,如上面的代码所示)。哦,IW_FIXED 只是一个将浮点数转换为定点数的宏。

当我尝试运行上述代码时,出现乘法溢出错误。调试值如下:

t = 0
u = 1 (which converts to 4096 in int32 fixed-point with IW_FIXED)
p0.x = 1638400 (400.0f with IW_FIXED_TO_FLOAT)
p0.y = 409600 (100.0f with IW_FIXED_TO_FLOAT)

老实说,我对定点数没有完全的了解。我理解这个想法,但是定点运算对我来说并不完全清楚(我一定已经放弃了大多数与基数 2 相关的数学课,真让我感到羞耻)。但是我完全被这样一个事实所震惊,像 1.0f * 400.0f 这样简单的东西会导致定点溢出。

所以,虽然我认为在我的项目中支持定点不会有问题,但似乎情况并非如此。该游戏是一个自上而下的汽车游戏,不会有巨大的轨道或任何东西,但它们必须至少与设备的屏幕一样大(或者更好的是,它的分辨率),因为我们的目标是对于平板电脑来说,像 1.0f * 400.0f 这样的问题意味着定点是不可能的。

我对这个假设是否正确?而且,对于未来的项目和其他有类似问题的人,我们如何评估项目中定点数的可行性?此外,如何在 16 位和 32 位之间做出决定将是一个很大的好处:)

(对不起,很长的帖子,感谢您的时间!)

更新:

所以,总结一下到目前为止的一些反应。理想的情况是以某种方式实现您的定点数,以便具有满足您需求的必要范围(Mooing Duck 的回答)。此外,对于 32 位数字的操作,最安全的做法是使用 64 位进行计算(timday 的答案和 Max 的评论)。顺便说一句,Marmalade 确实有一些“安全的固定乘法”函数,但对于 CIwVec2 的标量乘法运算符重载(它IW_FIXED_MUL在下面使用,它不能安全地乘法),情况并非如此。

最后,关于我的特定场景,似乎从 Marmalade 6.1 开始,仅使用浮点数可能是最好的解决方案。

编辑: 虽然 Max 的回答确实解决了我的问题,但这主要是因为它是果酱特有的东西。正因为如此,我选择了 Mooing Duck 的答案作为选择的答案,因为我觉得它通常会帮助更多的人。

4

3 回答 3

2

定点可以看成是存储分数的上半部分,你的情况下半部分是4096。所以1.0f等于4096/4096,它在内存中存储为4096。我不再赘述分数乘法,因为我不记得它们,但重要的部分是当乘法A/D * B/D得到结果C/D时,然后CA*B/D

所以:在你拥有1.0f * 400.0f的地方,计算机将其视为4096 * 1638400 / 4096。一个小代数表明这应该导致 1638400 (400.0f),但如果小数点库对此不够聪明,它会在进行除法之前得到一个临时值 6710886400,这对于int.

由于您的浮点数的分母为 4096,因此您的“浮点数”精确到最接近的 0.00024,范围为 -524288.999f 到 524287.999f(ish)。您的编译器中有没有办法降低“浮点数”的准确性以获得更大的范围?否则你就完蛋了。

编辑

Max 确认 4096 是 Marmalade 的一部分,无法更改。在这种情况下,我看到几个选项:
(1)不要使用定点。
(2) 尽量将所有浮点数保持在 -128 和 128 之间。
(3) 使用特殊函数将标量与下面CIwVec2使用的 a 相乘IW_FIXED_MUL。最好将 包装float在一个特殊的类中并重载operator*to 调用IW_FIXED_MUL。不要提供对 的隐式转换float,否则您将永远找不到所有错误。

于 2012-10-02T22:33:01.790 回答
2

您所看到的以及它所暗示的 32 位中间体(请参阅 Mooing Duck 的回答)对我来说似乎是一个严重的弱点。

我过去曾成功使用过定点库(在 386/486 时代的 x86 上),并且在校正移位之前它们都使用 64 位中间体进行乘法运算(事实上,我似乎记得英特尔提供了一个非常好的 32x32-to-64位乘法指令非常适合此)。较高的中间精度为您在数据范围上带来了很大的自由度;我真的很讨厌使用像这个 Marmalade lib 似乎受到限制的东西,但可以理解为什么他们可能出于可移植性的原因不得不这样做。

于 2012-10-02T22:44:23.790 回答
0

今天在移动开发中使用定点是没有意义的,当您使用 Marmalade 6.1 时 Marmalade 6.1 于 9 月发布。

很少有没有 FPU 的 Android 手机,但大多数手机都有 FPU、带有 GLES 1.0 和 GLES 2.0 的 GPU 我们现在针对带有果酱的 GLES 2.0 设备。

Marmalade 有 IW_FIXED 的原因是他们有自己的称为 IwGX 的图形层。

2012 年 9 月之前(果酱 6.1)。IwGX 不支持浮点数。这就是为什么开发人员即使针对最近的 GLES 2.0 设备也被迫使用固定数字。

今天在果酱中使用固定数字没有任何好处。

--

无论如何,如果您仍然希望使用固定点进行多重处理,则有IW_FIXED_MUL功能

添加:示例中的代码是正确的。

于 2012-10-02T22:35:11.787 回答