Float angle != angle什麼時候爲true?
[TOC]
看到一段代碼,頓時就呆了
if (angle != angle) { return XMQuaternionIdentity(); }
這樣寫的用意到底是啥呢?
源代碼
代碼很好理解,就是求兩個向量的四元數表示的旋轉角度
DirectX::XMVECTOR SimStarMan::QuaternionFromToRotation(const XMVECTOR& from, const XMVECTOR& to) { XMVECTOR quatRet; XMVECTOR axis, fromN, toN; float angle; fromN = XMVector3Normalize(from); toN = XMVector3Normalize(to); const XMVECTOR epsilon = XMLoadFloat3(&XMFLOAT3(0.00001f, 0.00001f, 0.00001f)); if (XMVector3NearEqual(fromN, toN, epsilon)) { return XMQuaternionIdentity(); } axis = XMVector3Cross(fromN, toN); axis = XMVector3Normalize(axis); angle = acosf(XMVectorGetX(XMVector3Dot(fromN, toN))); if (angle != angle) { return XMQuaternionIdentity(); } quatRet = XMQuaternionRotationAxis(axis, angle); quatRet = XMQuaternionNormalize(quatRet); return quatRet; }
探究
NAN
參考[2]中解釋了,說IEEE標準規定,NaN和任何數比較都是false,所以只有當angle爲NaN的時候, angle != angle才爲true
float a = acos(2); cout << "a = " << a << endl; // a = nan cout << (a != a) << endl; // 1
向量可能有問題
比如某個向量是無限長,正如 XMVector3Normalize 文檔提到的一樣[3]
For a vector of length 0, this function returns a zero vector. For a vector with infinite length, it returns a vector of QNaN.
acosf會出現Nan的情況
如果輸入範圍有問題,那麼結果就會出現NaN,第二條提到了[4]
If no errors occur, the arc cosine of arg (arccos(arg)) in the range [0 ; π], is returned. If a domain error occurs, an implementation-defined value is returned (NaN where supported). If a range error occurs due to underflow, the correct result (after rounding) is returned.
如何寫更優雅?
參考[2]中有個評論是這麼回答的:
This answer should be updated since std::isnan is now part of the C++11 standard and support has spread out. std::isnan was implemented in Visual Studio starting with Visual Studio 2013.
所以,上面的寫法是不是寫成下面這樣,會更加清晰明瞭:
// old angle ! = angle // new std::isnan(angle)
至此,這個問題算是清楚了,那麼NaN到底是如何定義的呢?
NaN定義
參考[5][6]
IEEE 754 floating point numbers can represent positive or negative infinity, and NaN (not a number).
小結:
- NaN在運算中得結果始終都是NaN,不像infinity有可能算出來一個正常值,比如 4/∞ = 0
- NaN的浮點數IEEE表示是一個範圍,節碼都是1,所以尾數表示的範圍都是NaN
Positive infinity is represented by the bit pattern X'7F80 0000'. Negative infinity is represented by the bit pattern X'FF80 0000'. A signaling NaN (NANS) is represented by any bit pattern between X'7F80 0001' and X'7FBF FFFF' or between X'FF80 0001' and X'FFBF FFFF'. A quiet NaN (NANQ) is represented by any bit pattern between X'7FC0 0000' and X'7FFF FFFF' or between X'FFC0 0000' and X'FFFF FFFF'. 31 | | 30 23 22 0 | | | | | -----+-+------+-+---------------------+ qnan 0 11111111 10000000000000000000000 snan 0 11111111 01000000000000000000000 inf 0 11111111 00000000000000000000000 -inf 1 11111111 00000000000000000000000 -----+-+------+-+---------------------+ | | | | | | +------+ +---------------------+ | | | | v v | exponent fraction | v sign
參考
[1] When is a float variable not equal to itself
[2] Checking if a double (or float) is NaN in C++
[7] IEEE User’s Guide [8] What is difference between quiet NaN and signaling NaN?