为什么浮点数的分辨率从原点开始进一步降低?


19

我的OpenGL场景中的对象离原点的距离非常可笑。当我查看这些对象并在它们周围平移/旋转/缩放照相机时,它们会“抖动”。也就是说,组成对象的顶点似乎围绕着虚构的3d点网格捕捉。我已经读到这是一个常见的问题,因为可以使用浮点精度(OpenGL和几乎所有其他功能使用的精度)存储大量信息。我不明白为什么会这样。

在寻找解决方案时,我遇到了非常简单的“浮动原点”修复程序,它似乎起作用了。我只是对所有物体进行了变换,所以我的对象处于相同的相对位置,但是无论照相机看着什么,它都接近原点。我在这里找到了一个解释:http : //floatingorigin.com/,但是我无法理解。

那么...有人可以解释为什么将我的场景放置在离原点很远的地方(例如1000万个单位)会导致我观察到的行为不稳定吗?还有为什么将其移到靠近原点的位置才能解决此问题?


4
因为如果没有,它们将是点数。重言式的问题,这个。
MSalters

1
是的,但是只有当您了解“浮点数”的真正含义时,才可以。
Kylotan '02

Answers:


26

由于浮点数在计算机中的表示方式,这就是全部。

整数存储起来非常简单。每个单位与“上一个”完全是“一个”,正如您期望的可数数字一样。

对于浮点数,情况并非如此。取而代之的是,几个位表示指数,其余的则表示所谓的尾数,即小数部分,然后将其乘以指数部分(隐式为2 ^ exp)以得出最终结果。

看看这里的数位视觉解释。

正是由于该指数是位的实际组成部分,所以一旦数字变大,精度便开始下降。

为了了解这一点,让我们在不深入了解的情况下进行仿浮点表示:采用像2这样的小指数并进行一些小数部分的测试:

2 * 2 ^ 2 = 8

3 * 2 ^ 2 = 12

4 * 2 ^ 2 = 16

...等等。

这些数字在仅指数2处并没有相差太远,但现在让我们尝试指数38:

2 * 2 ^ 38 = 549755813888

3 * 2 ^ 38 = 824633720832

4 * 2 ^ 38 = 1099511627776

哇,现在差很大!

该示例虽然没有专门介绍“非常下一个计数”(这将是下一个小数部分,具体取决于它的位数),但该示例演示了一旦数字变大,精度损失。浮点数中的“下一个可计数”单位非常小,指数较小,而对于大指数则非常大,而整数则始终为1。

浮点原点方法起作用的原因是因为它会将所有这些可能的大指数浮点数向下缩放到小指数,因此“下一个可计数”(精度)可以非常小且令人满意。


您提供的示例确实具有说明性,谢谢:)
Pris

3
在正确的轨道上,但我希望您使用的示例更接近于浮点的实际工作方式。它不会将尾数提高到指数;它是尾数* 2 ^指数。
内森·里德

3
你说得对,我知道;我不知道我在想什么。编辑了我的答案。

1
@ScottW好编辑!+1
内森·里德


8

必须提出该领域的经典著作:每位计算机科学家都应了解浮点数

但是其要点与单精度(双精度)浮点数只是一个32位(64位)二进制数(其中1位代表符号),基数2的8位(11位)指数如何相关,以及23位(52位)有效数字(括号是double的值)。

这意味着您可以用单精度表示的最小正数是0.0000000000000000000001 x 2 -127 = 2 -22 x 2 -127 = 2 -149〜1.40 x 10 -45

下一个正数是两倍:0.0000000000000000000010×2 -127 = 2 -148〜2.80×10 -45,然后下一个数是前两个0.0000000000000000000011×2的总和-127 = 3×2 -149〜4.2 - 45

这继续通过相同的常数差增加直到:0.1111111111111111111111×2 -127 = 2 -126 - 2 149〜1.17549435×10 -38 - 0.00000014×10 -38 = 1.17549421×10 -38

现在您已经到达正常数字(有效数字的第一个数字为1):1.0000000000000000000000 x 2 -126 = 2 -126 = 1.17549435 x 10 -38然后下一个数字是1.0000000000000000000001 x 2 -126 = 2 -126(1 + 2 -22)= 1.17549435 x 1.00000023。


2

浮点数离原点越来越不精确的原因是因为假定浮点数能够表示大数。完成此操作的方式将其称为“浮点”。它将可能的值(由其位长确定)进行拆分,以使每个指数具有大约相同的数字:对于32位浮点数,其中23位定义了尾数或有效位数。因此,它将能够在每个指数范围内采用2 ^ 23个不同的值。这些指数范围之一是1-2 [2 ^ 0到2 ^ 1],因此将范围1到2分成2 ^ 23个不同的值可以提高精度。

但是将范围[2 ^ 10到2 ^ 11]分成2 ^ 23个不同的值意味着每个值之间的间隔要大得多。如果不是,那么23位是不够的。整个过程都是一个折衷方案:您需要无限数量的位来表示任何实数。如果应用程序的工作方式使您无法以较低的精度获取较大的值,并且可以实际表示较大的值而受益,则可以使用浮点表示形式。


只是在7年后的进一步审查中在这里做笔记...我在示例中的数字并不是特别理想。但是总的观点是正确的。
史蒂文·卢

1

提供浮点精度工作原理的特定示例可能有点困难。为了补充其他答案,这里是一个。假设我们有一个十进制浮点数,尾数为三位,指数为一位:

尾数×10 指数

当指数为0时,可以精确表示0-999范围内的每个整数。当它为1时,实际上是将该范围的每个元素乘以10,因此得到的范围是0–9990;但是现在,只能精确表示10的倍数,因为您仍然只有3位数的精度。当指数最大为9时,每对可表示整数之间的差为10亿。您实际上是在为范围交易精度。

它与二进制浮点数的工作方式相同:每当指数上升1时,范围就会加倍,但该范围内可表示的值的数量将减半。这也适用于小数,这当然是问题的根源。


0

通常,分辨率会变差,因为分辨率乘以指数值(2 **指数部分)。

承认乔什的评论:以上只是将答案简明扼要。当然,正如我试图在http://floatingorigin.com/上指出的那样,这只是朝着整体解决方案迈进的一步,您的程序可能会在很多地方出现抖动:在精密管道或代码的其他部分。


这不会添加其他答案中尚未出现的任何内容。

是的:我意识到我可以将答案描述成一行,并认为有人可能会找到一个简洁的答案。
克里斯·索恩

-1

OpenGL深度缓冲区不是线性的。您走得越远,分辨率就越差。我建议读这篇。从那里取的东西(12.070):

总而言之,透视图划分的本质是,在视图体积的前部附近而不是后部附近导致更高的Z精度。

还有一个(12.040):

您可能以严重限制深度缓冲区精度的方式配置了zNear和zFar剪切平面。通常,这是由于zNear裁剪平面值太接近0.0引起的。随着zNear裁剪平面的设置越来越接近0.0,深度缓冲区的有效精度急剧下降。将zFar裁剪平面移到离眼睛更远的位置始终会对深度缓冲区的精度产生负面影响,但它不如将zNear裁剪平面移动到那样剧烈。

因此,您应将最靠近的裁剪平面移动到尽可能远的地方,将最远的裁剪平面移动到尽可能远的地方。


-1:问题是关于浮点精度,而不是非线性深度缓冲区表示的精度问题。
内森·里德

我看到的可能是由于深度缓冲问题。我在OpenGL的顶部使用了一个lib来查看场景,并假设它设置了摄影机,视图以及远近裁剪平面,以考虑几何图形的大小和位置(因为观众工具似乎自动为场景内容设置了最佳视图)。但我想可能并非如此-我将尝试使用剪切平面,使原始位置保持不变,然后看看会发生什么。
2012年

2内森·里德(Nathan Reed):作者写道,他有OpenGL场景,所以我想,也可能是这个问题。
zacharmarz 2012年

这个问题看似相似或相关,但是深度缓冲区值绝对不会以与浮点数兼容的方式存储。它是定点格式。因此,答案可能会产生误导。
史蒂文·卢
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.