1

I have a switch statement that assigns a value to an array of two dimensions depending on the case. The problem is that sometimes the cases are skipped and the default case is called. There seems to be no pattern to the functioning of the switch (sometimes works and sometimes goes to default).

I have tried changing the type of the value that I am giving to the switch from float to string because, in other questions that I saw before, the problem was the data type.

I also checked that the value that I was giving to the switch was one of the cases and I debug.log the "code"(variable of the value that I am giving to my switch) and it is always one of the cases.

I even changed the switch statement to a chain of if-else statements to see if I wrote the switch wrong but that didn't help.

The following code is a function that is generating the "code" that is given to the switch function that goes to default.

private void NodeEnumeration()
{
    //for each node in _mapArray
    for(int i = 0; i < _mapSize; i++)
    {
        for(int e = 0; e < _mapSize; e++)
        {
            //if node value is not 0
            if(_mapArray[i, e] != 0)
            {
                float code = 0f;
                //Left
                if(e != 0)
                {
                    _surroundingNodesPositions[0].x = e - 1;
                    _surroundingNodesPositions[0].y = i;

                    if(_mapArray[(int)_surroundingNodesPositions[0].y, (int)_surroundingNodesPositions[0].x] > 0)
                    {
                        code += 1f;
                    }
                }
                //Up
                if(i != 0)
                {
                    _surroundingNodesPositions[1].x = e;
                    _surroundingNodesPositions[1].y = i - 1;
                    if(_mapArray[(int)_surroundingNodesPositions[1].y, (int)_surroundingNodesPositions[1].x] > 0)
                    {
                        code += 0.1f;
                    }
                }
                //Right
                if(e != _mapSize - 1)
                {
                    _surroundingNodesPositions[2].x = e + 1;
                    _surroundingNodesPositions[2].y = i;
                    if(_mapArray[(int)_surroundingNodesPositions[2].y, (int)_surroundingNodesPositions[2].x] > 0)
                    {
                        code += 0.01f;
                    }
                }
                //Down
                if(i != _mapSize - 1)
                {
                    _surroundingNodesPositions[3].x = e;
                    _surroundingNodesPositions[3].y = i + 1;
                    if(_mapArray[(int)_surroundingNodesPositions[3].y, (int)_surroundingNodesPositions[3].x] > 0)
                    {
                        code += 0.001f;
                    }
                }

                AssignValue(i, e, code);//! Here is the problem
            }
        }
    }
}

private void AssignValue(int i, int e, float code)
{
    switch(code)
    {
        case 1.111f:
            _mapArray[i, e] = 1;
        break;

        case 1.110f:
            _mapArray[i, e] = 2;
        break;

        case 1.101f:
            _mapArray[i, e] = 3;
        break;

        case 1.100f:
            _mapArray[i, e] = 4;
        break;

        case 1.011f:
            _mapArray[i, e] = 5;
        break;

        case 1.010f:
            _mapArray[i, e] = 6;
        break;

        case 1.001f:
            _mapArray[i, e] = 7;
        break;

        case 1.000f:
            _mapArray[i, e] = 8;
        break;

        case 0.111f:
            _mapArray[i, e] = 9;
        break;

        case 0.110f:
            _mapArray[i, e] = 10;
        break;

        case 0.101f:
            _mapArray[i, e] = 11;
        break;

        case 0.100f:
            _mapArray[i, e] = 12;
        break;

        case 0.011f:
            _mapArray[i, e] = 13;
        break;

        case 0.010f:
            _mapArray[i, e] = 14;
        break;

        case 0.001f:
            _mapArray[i, e] = 15;
        break;

        case 0.000f:
            _mapArray[i, e] = 16;
        break;

        default:
            Debug.LogError("MapGenerator::NodeEnumeration()::AssignValue() no value can be assigned | code: " + code);
            Debug.Log("height: " + i + " width: " + e);
        break;
    }
}
Roman
  • 13
  • 2
  • 2
    Don't use floats at all for this. `code` should be of type `int`. Just use a more natural mapping of bit fields to `_mapArray` values and you won't need a `switch` statement at all. Do you understand bit fields? Use 1, 2, 4, and 8 as your += values. These will naturally sum up to 16 unique values in the range 0 through 15. – Wyck Aug 23 '22 at 20:05

1 Answers1

2

The problem that you're dealing with is a result of floating point (im)precision. Read through the answer here, as it explains it a lot better that I could do.


TLDR: you can't rely on decimal math to be precise, even simple equations such as 0.1f + 0.2f != 0.3f. In most cases, you should just use integer math, or epsilons for judging if two floating point values are equal:

private static float EPSILON = 0.0005f;
public static bool AreApproximatelyEqual(float a, float b)
{
    return Mathf.Abs(a - b) < EPSILON;
}

However, in your case specifically, we can do a LOT better than just using floating point epsilons, or even integer math. Using boolean math is the best way to go about this, with a concept called "flags".

Notice how the final value assigned into the _mapArray is always equal to 16 - value (when removing the decimal point, and converting the value to binary). Example: 1.111f = 0b1111 in binary, = 15 in decimal. 16-15 = 1, your intended value.

Since this holds true for all of your cases, we can set each bit within the using the OR operation:

    0100
|=  0010
---------
    0110

The final implementation then would look something like this:

private void NodeEnumeration()
{
    //for each node in _mapArray
    for(int i = 0; i < _mapSize; i++)
    {
        for(int e = 0; e < _mapSize; e++)
        {
            //if node value is not 0
            if(_mapArray[i, e] != 0)
            {
                int code = 0; //change our code to be an integer. we could go all the way down to a byte, if you wanted.
                //Left
                if(e != 0)
                {
                    _surroundingNodesPositions[0].x = e - 1;
                    _surroundingNodesPositions[0].y = i;

                    if(_mapArray[(int)_surroundingNodesPositions[0].y, (int)_surroundingNodesPositions[0].x] > 0)
                    {
                        code |= 0b1000; //set the 4th bit in this case. Equivalent to += 8
                    }
                }
                //Up
                if(i != 0)
                {
                    _surroundingNodesPositions[1].x = e;
                    _surroundingNodesPositions[1].y = i - 1;
                    if(_mapArray[(int)_surroundingNodesPositions[1].y, (int)_surroundingNodesPositions[1].x] > 0)
                    {
                        code |= 0b0100; //set the 3rd bit in this case. Equivalent to += 4
                    }
                }
                //Right
                if(e != _mapSize - 1)
                {
                    _surroundingNodesPositions[2].x = e + 1;
                    _surroundingNodesPositions[2].y = i;
                    if(_mapArray[(int)_surroundingNodesPositions[2].y, (int)_surroundingNodesPositions[2].x] > 0)
                    {
                        code |= 0b0010; //set the 2nd bit in this case. Equivalent to += 2
                    }
                }
                //Down
                if(i != _mapSize - 1)
                {
                    _surroundingNodesPositions[3].x = e;
                    _surroundingNodesPositions[3].y = i + 1;
                    if(_mapArray[(int)_surroundingNodesPositions[3].y, (int)_surroundingNodesPositions[3].x] > 0)
                    {
                        code |= 0b0001; //Set the 1st bit in this case. Equivalent to += 1
                    }
                }

                _mapArray[i, e] = 16 - flags;
            }
        }
    }
}
ipodtouch0218
  • 674
  • 1
  • 2
  • 13
  • Or use *decimal* floating point rather than *binary* floating point. – Jon Skeet Aug 23 '22 at 19:57
  • 1
    This is off track from the fundamental problem. OP is trying to store bit fields in a float. They don't need an _AreApproximatelyEqual_ function, they need to store their bits in an integer type. – Wyck Aug 23 '22 at 19:58
  • @Wyck just addressed this. I feel as though teaching floating point's imprecision will prevent them from running into a situation like this in the future, rather than just giving them the fix first. – ipodtouch0218 Aug 23 '22 at 20:00
  • Thanks for the answers, I will convert it to an integer, see if it works and update you on the state of this problem. – Roman Aug 23 '22 at 20:02
  • @Roman consider using bit flags instead (the bottom half of my answer) in this case! Using bits to represent booleans like this (typically called flags) is a technique seen very often, and helps save on memory usage! (especially at scale) – ipodtouch0218 Aug 23 '22 at 20:07
  • That was the problem, thank you very much. I will read the floating point's imprecisions link now so it doesn't happen again. – Roman Aug 23 '22 at 20:09