Skip to content

fix(gtx): extractRealComponent returns the canonical (non-negative) real part - #1446

Open
Osamaali313 wants to merge 2 commits into
g-truc:masterfrom
Osamaali313:fix/extract-real-component-sign
Open

fix(gtx): extractRealComponent returns the canonical (non-negative) real part#1446
Osamaali313 wants to merge 2 commits into
g-truc:masterfrom
Osamaali313:fix/extract-real-component-sign

Conversation

@Osamaali313

Copy link
Copy Markdown

Problem

glm::extractRealComponent reconstructs a quaternion's real component from its imaginary parts as sqrt(1 - x² - y² - z²), but it returns the negative root:

template<typename T, qualifier Q>
GLM_FUNC_QUALIFIER T extractRealComponent(qua<T, Q> const& q)
{
    T w = static_cast<T>(1) - q.x * q.x - q.y * q.y - q.z * q.z;
    if(w < T(0))
        return T(0);   // clamp: keep the result non-negative...
    else
        return -sqrt(w);   // ...then negate it (?)
}

The function is internally contradictory: it clamps the radicand with if (w < 0) return 0 — signalling a non-negative magnitude — and then returns -sqrt(w), which is always ≤ 0. So for any canonical unit quaternion (w ≥ 0) it returns the wrong sign:

quaternion (w, x, y, z) true real part extractRealComponent
60° about X → (0.8660254, 0.5, 0, 0) +0.8660254 −0.8660254
identity → (1, 0, 0, 0) +1 −1

This contradicts the documented purpose ("Extract the real component of a quaternion") and the standard reconstruction convention (the canonical real part of a unit quaternion is the non-negative root — the same w = sqrt(1 - x² - y² - z²) used when reconstructing a quaternion stored as x/y/z only).

Fix

Return the positive root (sqrt(w)).

Tests

The function had no test coverage (which is why the sign error went unnoticed). Added test_extractRealComponent to test/gtx/gtx_quaternion.cpp: it builds a canonical unit quaternion, extracts the real component, and asserts it recovers the true w and is non-negative. (Fails on the old -sqrt, passes on the fix.)

…eal part

extractRealComponent reconstructs a quaternion's real component from its
x/y/z as sqrt(1 - x^2 - y^2 - z^2), but returned the NEGATIVE root. The
function already clamps the radicand with `if (w < 0) return 0`, signalling
a non-negative result, yet then negates it -- so for any canonical unit
quaternion (w >= 0) it returns the wrong sign (e.g. a 60-degree rotation
about X has true w = +0.8660254 but the function returned -0.8660254; the
identity quaternion returns -1 instead of +1).

Return the positive root, matching the documented purpose ("extract the
real component") and the standard reconstruction convention. Adds a
regression test -- the function had no test coverage.
Copilot AI review requested due to automatic review settings June 15, 2026 16:39

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Fixes the sign of glm::extractRealComponent so it returns the canonical (non-negative) real component, and adds a regression test to prevent returning the negated value.

Changes:

  • Corrected extractRealComponent to return sqrt(w) instead of -sqrt(w) for valid inputs.
  • Added a new unit test validating that extraction returns a non-negative w and matches the canonical quaternion.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
test/gtx/gtx_quaternion.cpp Adds a regression test covering extractRealComponent’s expected (non-negative) output.
glm/gtx/quaternion.inl Fixes the sign bug in extractRealComponent by removing the unintended negation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread test/gtx/gtx_quaternion.cpp Outdated
Comment on lines +105 to +112
// A canonical unit quaternion (60 degrees about X): w = cos(30 deg) >= 0.
glm::quat const q(0.8660254f, 0.5f, 0.0f, 0.0f);
float const w = glm::extractRealComponent(q);

// Reconstructing the real component from x/y/z must recover the canonical
// (non-negative) w, not its negation.
Error += glm::epsilonEqual(w, q.w, 0.0001f) ? 0 : 1;
Error += (w >= 0.0f) ? 0 : 1;
Per review: replace the hard-coded quaternion literals with
glm::angleAxis(glm::radians(60), X) and compare against glm::cos(Angle/2),
so the test is self-describing and free of magic numbers.
@Osamaali313

Copy link
Copy Markdown
Author

Thanks @copilot — done in 8b954a7. The test now builds the quaternion with glm::angleAxis(glm::radians(60.0f), glm::vec3(1, 0, 0)) and compares the extracted real component against glm::cos(Angle * 0.5f), so there are no magic-number literals and the intent is self-describing. (angleAxis is already used elsewhere in this test file, so no new include is needed.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants