Search code examples
c++logic

How is the logical || operation handled in C++?


Let's say I have an if-statement with an unfortunate amount of || checks. Does the CPU go through all the checks before summing it up to an either false or true bool, or does it terminate and return true as soon as it finds a true bool?

I am wondering because of the 3d object frustum culling of boxes requiring 6 different checks, that you can either do as consecutive if statements, or as a single if statement with multiple || in it, wherein if it goes through all the checks even though as early as the first check returns true, then it's better in the long run for the program to use multiple if statements instead of a single one, as it leads to less calculations during runtime.

Here's the part of the frustum culling code for a "generic cube object with any side length" wherein this is of interest. I'll post both versions, first is using only if statements, the second using a single if:

for (int i = 0; i < 6; i++)
    {
        if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC-r, yC-r, zC-r})) >= 0.0f) continue;
        if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC-r, yC-r, zC+r})) >= 0.0f) continue;
        if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC-r, yC+r, zC-r})) >= 0.0f) continue;
        if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC-r, yC+r, zC+r})) >= 0.0f) continue;
        if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC+r, yC-r, zC-r})) >= 0.0f) continue;
        if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC+r, yC-r, zC+r})) >= 0.0f) continue;
        if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC+r, yC+r, zC-r})) >= 0.0f) continue;
        if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC+r, yC+r, zC+r})) >= 0.0f) continue;

        return false;
    }
for (int i = 0; i < 6; i++)
    {
        if ((DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC-rX, yC-rY, zC-rZ})) >= 0.0f) ||
            (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC-rX, yC-rY, zC+rZ})) >= 0.0f) ||
            (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC-rX, yC+rY, zC-rZ})) >= 0.0f) ||
            (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC-rX, yC+rY, zC+rZ})) >= 0.0f) ||
            (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC+rX, yC-rY, zC-rZ})) >= 0.0f) ||
            (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC+rX, yC-rY, zC+rZ})) >= 0.0f) ||
            (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC+rX, yC+rY, zC-rZ})) >= 0.0f) ||
            (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC+rX, yC+rY, zC+rZ})) >= 0.0f)) continue;

        return false;
    }```

Solution

  • Alright, thanks to the comments I received, I can now determine that the two variations are in fact equal to each other in this case, as both will "short circuit" as soon as one check returns true. This means that I can drag the single if-statement variant out into a different function that returns the result of the if statement, and use that function as the condition in a single if statement in both types of box frustum culling (box with all sides equal, and box with differing side lengths).

    So, instead of having:

    bool FrustumClass::CheckCube(float xC, float yC, float zC, float r)
    {
        // Check if any one point of the cube is in the view frustum.
        for (int i = 0; i < 6; i++)
        {
            if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC-r, yC-r, zC-r})) >= 0.0f) continue;
            if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC-r, yC-r, zC+r})) >= 0.0f) continue;
            if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC-r, yC+r, zC-r})) >= 0.0f) continue;
            if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC-r, yC+r, zC+r})) >= 0.0f) continue;
            if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC+r, yC-r, zC-r})) >= 0.0f) continue;
            if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC+r, yC-r, zC+r})) >= 0.0f) continue;
            if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC+r, yC+r, zC-r})) >= 0.0f) continue;
            if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC+r, yC+r, zC+r})) >= 0.0f) continue;
    
            return false;
        }
    
        return true;
    }
    
    bool FrustumClass::CheckRectangle(float xC, float yC, float zC, float rX, float rY, float rZ)
    {
        // Check if any of the 6 planes of the rectangle (AABB/OBB) are inside the view frustum.
        for (int i = 0; i < 6; i++)
        {
            if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC-rX, yC-rY, zC-rZ})) >= 0.0f) continue;
            if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC-rX, yC-rY, zC+rZ})) >= 0.0f) continue;
            if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC-rX, yC+rY, zC-rZ})) >= 0.0f) continue;
            if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC-rX, yC+rY, zC+rZ})) >= 0.0f) continue;
            if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC+rX, yC-rY, zC-rZ})) >= 0.0f) continue;
            if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC+rX, yC-rY, zC+rZ})) >= 0.0f) continue;
            if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC+rX, yC+rY, zC-rZ})) >= 0.0f) continue;
            if (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(&frustumPlanes[i]), {xC+rX, yC+rY, zC+rZ})) >= 0.0f) continue;
    
            return false;
        }
    
        return true;
    }
    

    I can have:

    bool FrustumClass::CheckCube(float xC, float yC, float zC, float r)
    {
        // Check if any one point of the cube is in the view frustum.
        for (int i = 0; i < 6; i++)
        {
            if (boxCheck(&frustumPlanes[i], xC, yC, zC, r, r, r)) continue;
    
            return false;
        }
    
        return true;
    }
    
    bool FrustumClass::CheckRectangle(float xC, float yC, float zC, float rX, float rY, float rZ)
    {
        // Check if any of the 6 planes of the rectangle (AABB/OBB) are inside the view frustum.
        for (int i = 0; i < 6; i++)
        {
            if (boxCheck(&frustumPlanes[i], xC, yC, zC, rX, rY, rZ)) continue;
    
            return false;
        }
    
        return true;
    }
    
    bool boxCheck(DirectX::XMFLOAT4* frustumPlane, float xC, float yC, float zC, float rX, float rY, float rZ)
    {
        return
        (
            (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(frustumPlane), { xC - rX, yC - rY, zC - rZ })) >= 0.0f) ||
            (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(frustumPlane), { xC - rX, yC - rY, zC + rZ })) >= 0.0f) ||
            (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(frustumPlane), { xC - rX, yC + rY, zC - rZ })) >= 0.0f) ||
            (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(frustumPlane), { xC - rX, yC + rY, zC + rZ })) >= 0.0f) ||
            (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(frustumPlane), { xC + rX, yC - rY, zC - rZ })) >= 0.0f) ||
            (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(frustumPlane), { xC + rX, yC - rY, zC + rZ })) >= 0.0f) ||
            (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(frustumPlane), { xC + rX, yC + rY, zC - rZ })) >= 0.0f) ||
            (DirectX::XMVectorGetX(DirectX::XMPlaneDotCoord(DirectX::XMLoadFloat4(frustumPlane), { xC + rX, yC + rY, zC + rZ })) >= 0.0f)
        );
    }
    

    Which is far easier to handle, imo.