Skip to content

Commit 65cf88a

Browse files
Merge pull request #205 from markuswntr/quaternion/careful-length
Account for overflow/underflow in Quaternion `length`
2 parents e29daf5 + 67ddbbe commit 65cf88a

File tree

2 files changed

+17
-1
lines changed

2 files changed

+17
-1
lines changed

Sources/QuaternionModule/Norms.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,19 @@ extension Quaternion {
7777
/// - `.lengthSquared`
7878
@_transparent
7979
public var length: RealType {
80+
let naive = lengthSquared
81+
guard naive.isNormal else { return carefulLength }
82+
return .sqrt(naive)
83+
}
84+
85+
// Internal implementation detail of `length`, moving slow path off
86+
// of the inline function.
87+
@usableFromInline
88+
internal var carefulLength: RealType {
8089
guard isFinite else { return .infinity }
81-
return .sqrt(lengthSquared)
90+
guard !magnitude.isZero else { return .zero }
91+
// Unscale the quaternion, calculate its length and rescale the result
92+
return divided(by: magnitude).length * magnitude
8293
}
8394

8495
/// The squared length `(r*r + x*x + y*y + z*z)`.

Tests/QuaternionTests/PropertyTests.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ final class PropertyTests: XCTestCase {
3434
XCTAssertEqual(Quaternion<T>.zero.length, .zero)
3535
XCTAssertEqual(Quaternion<T>(real: .zero, imaginary: -.zero).length, .zero)
3636
XCTAssertEqual(Quaternion<T>(real: -.zero, imaginary: -.zero).length, .zero)
37+
// Check for overflow and underflow when calculating the length
38+
XCTAssertEqual(Quaternion<T>(real: .greatestFiniteMagnitude, imaginary: 0, 0, 0).length, .greatestFiniteMagnitude)
39+
XCTAssertEqual(Quaternion<T>(real: 0, imaginary: -.greatestFiniteMagnitude, 0, 0).length, .greatestFiniteMagnitude)
40+
XCTAssertEqual(Quaternion<T>(real: 0, imaginary: 0, .leastNormalMagnitude, 0).length, .leastNormalMagnitude)
41+
XCTAssertEqual(Quaternion<T>(real: 0, imaginary: 0, 0, -.leastNormalMagnitude).length, .leastNormalMagnitude)
3742
// The properties of pure and real
3843
XCTAssertTrue(Quaternion<T>.zero.isPure) // zero quaternion is both, pure...
3944
XCTAssertTrue(Quaternion<T>.zero.isReal) // and real

0 commit comments

Comments
 (0)