从数学角度来看,零向量无法被规范化。它的长度将始终为0
。
对于给定向量v = (v1,v2,...,vn)
,我们有:||v|| = sqrt(v1^2 + v2^2 + ... + vn^2)
。让我们记住,规范化向量是具有||v||=1
的向量。
因此,对于v = 0
,我们有:||0|| = sqrt(0^2 + 0^2 + ... + 0^2) = 0
。你永远不能对其进行规范化。
还要注意,为确保一致性,不应返回NaN
或任何其他空值。零向量的规范形式确实是v=0
。
比Yuval所说的还要糟糕。
从数学上讲,给定一个向量x,你要寻找一个新的向量x/||x||
其中||.||是范数,你可能认为它是带有欧几里得范数的
||.|| = sqrt(dot(v,v)) = sqrt(sum_i x_i**2)
这些都是浮点数,所以仅仅防止除以零是不够的,如果x_i都很小(可能会下溢而失去幅度),也会出现浮点问题。
基本上,如果您真的需要能够正确处理小向量,那么您将需要做更多的工作。
如果在您的应用程序中小向量和零向量没有意义,则可以针对向量的幅度进行测试并执行适当的操作。
(请注意,一旦您开始处理浮点数,而不是实数,执行诸如平方然后取平方根或它们的和之类的操作在可表示范围的两端都存在问题)
底线:在所有情况下正确地进行数值计算比看起来更加棘手。
例如,以下是对这个(归一化)操作进行的天真方式的潜在问题:
正如已经多次提到的那样,您无法对零向量进行归一化。因此,您有以下选择:
选项4不是很好,因为某些语言(例如C)没有异常,并且归一化向量通常在非常低级别的代码中发现。抛出异常相当昂贵,任何可能想要处理零/小向量情况的代码都会在发生这种情况时受到不必要的性能影响。
选项1存在问题,返回值将没有单位长度,因此它可能会在调用代码中默默引入错误。
选项2与选项1类似,但由于NaN通常比零更易于注意到,因此它可能更容易表现出来。
我认为选项3是最佳解决方案,尽管它确实使接口更加复杂。而不是说
vec3 = myVec.normalize();
现在你需要说类似于以下内容:
vec3 result;
bool success = myVec.normalize(&result);
if(success)
// vector was normalized
else
// vector was zero (or small)
myVec.normalize(&result);
并丢弃 success
位。一个好的方法是依赖于“语言本地的0/0方式”,即,
vector normalize(){ |
if(this->length()==0)return vector(0/0); |
// ...计算... |
} - user16829600(0,0,0) 应该是 (0,0,0) 归一化加上一个警告(或异常)也许。
从数学上讲,我想它是没有定义的。
零向量已经被规范化,根据我遇到的任何矢量范数定义,这就是一个已处理的情况。
至于分量之和为零的向量-好吧,这取决于您使用的范数定义。 对于普通的L2范数(原点和向量之间的欧几里德距离),计算规范化向量的标准公式应该可以正常工作,因为它首先平方了每个分量。
给定一个向量v,将其归一化意味着保持其方向并通过乘以一个精心选择的因子使其成为单位长度。
对于零向量来说,这显然是不可能的,因为它实际上没有方向,或者因为它的长度不能通过乘以某个因子来改变(它总是等于零)。
我建议,无论您想要使用向量的任何过程,并且需要将该向量归一化,都不适用于零向量。
这完全取决于你如何定义“归一化”。该术语的一个可能扩展是说,此操作的结果是任何单位长度向量(我在这里主要使用(1,0,0))。例如,当您需要将归一化返回到给定点的圆边界方向时,这非常有用。