So a few weeks ago I wrote some code that would align my text in a rectangle. The alignment would be either top left, top center, top right, middle left, etc… After finishing that up, I noticed that my text looks bad and I figured that it was due to floating point rounding errors. This happens when you set a sprite position to something like

Vector2 position = Vector2(100.5f, 50.2f);

The problem here is that the smallest controllable element of a picture represented on the screen is a single pixel, and what you are trying to do is set your image, sprite, text or whatever to one hundered pixels and a half. There is no such thing as half a pixel. So your graphics card tries to correct that and in doing so you get artifacts like these

 

 

Notice how there are lines in between each sprite, that happens because the sprite position is not exact. The sprite position is not set to Vector2(100, 50). It is set to something in between like Vector2(100.5f, 50.5f) pixel. That causes artifacts like the lines you see above. So, how do you solve this? Either cast your floating point positions to ints then back to floats or use a function like Round or Floor to round your numbers up or down to whole numbers.

 

So let’s take a look at my text

As you can see, the text above is a lot cleaner than the text below. The text below looks like it has artifacts (which it does). Notice the letters “u”, “F”, “m”, “i”, and “e”. They all look bad. That is because these letters position is not exact. So how did I fix that? I cast my floats into ints, did my division calculations to align the text inside an imaginary rectangle and I cast those ints back to floats.

The day I wrote that code, I knew it was temporary and I would change it later on. I did not like the fact that I had to cast my numbers not once, but twice. At the time I didn’t think it had any impact on my engine performance, but oh boy was I wrong.

So a few days ago I was going through my YouTube subscription box and I found a video from CppCon about how unsigned integers are much slower than signed integers. After hours of research, I came across this article. Why you should never cast a Floats to Ints. After reading that I remembered the block I wrote that would cast my text position from float to an int back to float again. Here it is.

 

Math::Vector2 Text::GetTextAlignmentOffset(TextAlignment textAlignment)
{
    Vector2 textSize = Vector2((float)rectangle.width, (float)rectangle.height);
    Vector2 scissorRectangleSize = Vector2((float)scissorRectangle.width, (float)scissorRectangle.height);
    return Vector2((float)((int)(scissorRectangleSize.Center().x - textSize.Center().x)), (float)((int)(scissorRectangleSize.Center().y - textSize.Center().y)));
}

This horribly looking code made me want to test what the above article said about how casting floats to ints is bad. So here is what I did. I created three tests like so.

int main()
{
    BF::System::Timer t;
    
    BF::Math::Rectangle rectangle(10, 10, 100, 200);
    BF::Math::Rectangle scissorRectangle(0, 0, 200, 300);
    
    //Test1
    //------------------------------------------------------------
    t.Reset();
    
    for (size_t i = 0; i < 10000000; i++)
    {
    	Vector2 textSize = Vector2((float)rectangle.width, (float)rectangle.height);
    	Vector2 scissorRectangleSize = Vector2((float)scissorRectangle.width, (float)scissorRectangle.height);
    	Vector2 pos1 = Vector2((float)((int)(scissorRectangleSize.x - textSize.x)), (float)scissorRectangle.y);
    }
    
    std::cout << t.GetElapsedTimeInMilliseconds() << std::endl;

    //Test2
    //------------------------------------------------------------
    t.Reset();
    
    for (size_t i = 0; i < 10000000; i++)
    {
    	int p = scissorRectangle.width - rectangle.width;
    	Vector2 pos2 = Vector2((float)p, (float)scissorRectangle.height);
    }
    
    std::cout << t.GetElapsedTimeInMilliseconds() << std::endl;

    //Test3
    //------------------------------------------------------------
    t.Reset();
    
    for (size_t i = 0; i < 10000000; i++)
    {
    	Vector2i pos3 = Vector2i(scissorRectangle.width - rectangle.width, scissorRectangle.height);
    }
    
    std::cout << t.GetElapsedTimeInMilliseconds() << std::endl;
    //------------------------------------------------------------
    
    return  0;
}

The first test casts “rectangle.width” and “rectangle.height” from integers to floats and stores them inside “textSize”. The same thing is done to “scissorRectangle” and it is stored inside “scissorRectangleSize “. After that, I subtract “scissorRectangleSize.x” from “textSize.x” and cast them from float to int and back to float again. Running this ten million times took 86.4108 milliseconds. I did 7 total casts in this test.

The second test I only did two casts. Running that also ten million times took 25.1934 milliseconds.

In the third and last test, I’ve done a total of zero casts since Vector2i is a vector with two ints instead of two floats for the x and y. Running this ten million times took 25.2048 milliseconds which is around the same amount of time the second test took.

 

Keep in mind all these tests were done on release build on x64.

So, there you have it. Doing this many casts in three lines of code is very bad.