Floats subtraction and comparisons

Pages: 123
SubZeroWins wrote:
Yes my comment has to do with sf::FloatRec as I have seen that used in a sample code.

getGlobalBounds() returns a sf::FloatRect. That's what left, top, width and height that you have been using belongs to. So you're already using sf::FloatRect even though you haven't spelled out the type name.

SubZeroWins wrote:
If you look at the <<<<<< arrows below in the "NOT" the glitch always happens when the difference is the smallest. Look at those same values in the longer list of "MOVE" section where the glitch does not happen and those differences are much larger.

Okay, so if I'm reading
NOT
dt = 0.015526
ImageTop marker if() check to move <= 1283.27<<<<<<<<<<<<<<<<<<<<<<<<
ImageBottom.left = 1283.35<<<<<<<<<<<<<<<<<<<<<<<<
ImageTop.left before move = 1343.27
correctly the top image shouldn't move because 1283.27 is less than 1283.35.

I want to look at two MOVEs before a NOT so I'm going to use an earlier output of yours:
MOVE
dt = 0.016525
ImageTop marker if() check to move <= 887.276
ImageBottom.left = 886.763
ImageTop.left before move = 947.276
ImageTop.left after move = 927.446 - 19.83 = 907.616

MOVE
dt = 0.017379
ImageTop marker if() check to move <= 867.446
ImageBottom.left = 865.908
ImageTop.left before move = 927.446
ImageTop.left after move = 906.592 - 20.8548 = 885.737

NOT
dt = 0.015978
ImageTop marker if() check to move <= 846.592
ImageBottom.left = 846.734
ImageTop.left before move = 906.592

On the first MOVE we don't know how much ImageBottom has moved but we can see that TopImage's poistion was changed by -19.83.

On the second MOVE we can see that ImageBottom has moved by -20.855 and TopImage has moved by -20.854

It looks like the two images move at almost the same speed.

It's interesting to note that the difference between the left and right side of <= goes from -0.513 to -1.538 to +0.142. At first I thought this number should stay roughly constant when the images move at the same speed but then I realized that only the left side of <= has been affected by the current dt value. That makes it susceptible to fluctuations in the dt value and explains why you're experiencing the problems that you are.
Last edited on
I always move the ImageBottom first before checking the if(<=), so shouldn't <= ALWAYS be true if the distance for both moves are equal and that there is no loss of precision? You seem to be more certain that it is not loss of precision? Even the getGlobalBounds() rounds off to the 3rd decimal place and loses some value, although minor. Maybe that combined with some loss in the subtraction and checking?

Below I added "MOVE BOTTOM" and "MOVE TOP" to show you precisely how the values and checks carry over. Proof that the dt and the distance to move is EXACTLY the same for both the top and bottom.

Just a FYI too, the .1f had been changed to .4f for all the previous cout's so that I can see them clearer on the screen. So (.4f * ImageTop.width()) = (.4f * 150) = 60.0f always, just in case you were wondering why the check marker had been off from the original .1f code.


MOVE BOTTOM
dt = 0.016616
ImageBottom.left before move = 1024.81
ImageBottom.left after move = 1024.81 - 19.9392 = 1004.87

MOVE TOP
dt = 0.016616
ImageTop marker if() check to move <= 1005.02
ImageBottom.left = 1004.87
ImageTop.left before move = 1065.02
ImageTop.left after move = 1065.02 - 19.9392 = 1045.08

MOVE BOTTOM
dt = 0.016647
ImageBottom.left before move = 1004.87
ImageBottom.left after move = 1004.87 - 19.9764 = 984.893

MOVE TOP
dt = 0.016647
ImageTop marker if() check to move <= 985.085
ImageBottom.left = 984.893
ImageTop.left before move = 1045.08
ImageTop.left after move = 1045.08 - 19.9764 = 1025.11

MOVE BOTTOM
dt = 0.016885
ImageBottom.left before move = 984.893
ImageBottom.left after move = 984.893 - 20.262 = 964.631

MOVE TOP
dt = 0.016885
ImageTop marker if() check to move <= 965.108
ImageBottom.left = 964.631
ImageTop.left before move = 1025.11
ImageTop.left after move = 1025.11 - 20.262 = 1004.85

MOVE BOTTOM
dt = 0.016497
ImageBottom.left before move = 964.631
ImageBottom.left after move = 964.631 - 19.7964 = 944.834

MOVE TOP
dt = 0.016497
ImageTop marker if() check to move <= 944.846
ImageBottom.left = 944.834
ImageTop.left before move = 1004.85
ImageTop.left after move = 1004.85 - 19.7964 = 985.05

MOVE BOTTOM
dt = 0.01704
ImageBottom.left before move = 944.834
ImageBottom.left after move = 944.834 - 20.448 = 924.386

MOVE TOP
dt = 0.01704
ImageTop marker if() check to move <= 925.05
ImageBottom.left = 924.386
ImageTop.left before move = 985.05
ImageTop.left after move = 985.05 - 20.448 = 964.602

MOVE BOTTOM
dt = 0.016273
ImageBottom.left before move = 924.386
ImageBottom.left after move = 924.386 - 19.5276 = 904.859

NOT*********************************************
dt = 0.016273
ImageTop marker if() check to move <= 904.602
ImageBottom.left = 904.859
ImageTop.left with no move = 964.602

MOVE BOTTOM
dt = 0.016663
ImageBottom.left before move = 904.859
ImageBottom.left after move = 904.859 - 19.9956 = 884.863

MOVE TOP
dt = 0.016663
ImageTop marker if() check to move <= 904.602
ImageBottom.left = 884.863
ImageTop.left before move = 964.602
ImageTop.left after move = 964.602 - 19.9956 = 944.606

MOVE BOTTOM
dt = 0.01671
ImageBottom.left before move = 884.863
ImageBottom.left after move = 884.863 - 20.052 = 864.811

MOVE TOP
dt = 0.01671
ImageTop marker if() check to move <= 884.606
ImageBottom.left = 864.811
ImageTop.left before move = 944.606
ImageTop.left after move = 944.606 - 20.052 = 924.554

MOVE BOTTOM
dt = 0.016608
ImageBottom.left before move = 864.811
ImageBottom.left after move = 864.811 - 19.9296 = 844.882

MOVE TOP
dt = 0.016608
ImageTop marker if() check to move <= 864.554
ImageBottom.left = 844.882
ImageTop.left before move = 924.554
ImageTop.left after move = 924.554 - 19.9296 = 904.625
With this pseudo code it can go on forever if you subtract equal increments to both and the <= will always be true. BUT if there is loss of precision somewhere at the end in C++, then no move for a frame until the next frame that sets the bottom image 20 pixels further to the left to insure that the next check will surely be lower than the top.

This is the way I like to see it and so I see it with some precision loss, but you tell me?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
float v1 = 1080.0001f;
float MarkerCheck = 1080.0002f;


if (v1 <= MarkerCheck)
//(1080.0001f <= 1080.0002f)
{
	//Move MarkerCheck - 20 (after Move = 1060.0002f)
	MarkerCheck -= 20;	
}

v1 -= 20;	// Move now = 1060.0001f




if (v1 <= MarkerCheck)
//(1060.0001f <= 1060.0002f)
{
	//Move MarkerCheck - 20 (after Move = 1040.0002f)
	MarkerCheck -= 20;	
}

v1 -= 20;	// Move now = 1040.0001f




if (v1 <= MarkerCheck)
//(1040.0001f <= 1040.0002f)
{
	//Move MarkerCheck - 20 (after Move = 1020.0002f)
	MarkerCheck -= 20;	
}

v1 -= 20;	//Move now = 1020.0001f




if (v1 <= MarkerCheck)
//(1020.0001f <= 1020.0002f)
{
	//Move MarkerCheck - 20 (after Move = 1000.0002f)
	MarkerCheck -= 20;	
}

v1 -= 20;	//Move now = 1000.0001f

//THIS CAN GO ON FOR ALL ETERNITY!!!!!!!!!!!!!!!


//BUT IF THERE IS PRECISION LOSS ANYWHERE, ESPECIALLY ACCUMULATIVE
if (1020.0001f <= 1019.9998f)
{
	//NO MOVE!!!!!!!!!!!!!!!!!!
}
Last edited on
SubZeroWins wrote:
I always move the ImageBottom first before checking the if(<=), so shouldn't <= ALWAYS be true if the distance for both moves are equal and that there is no loss of precision?

In other words, when you reach the if statement you have only moved ImageBottom but not yet moved ImageTop.

If you where to print the result of the <= expression at the end of each frame (after you have moved both ImageBottom and ImageTop) then I think you would see it is often false. It becomes true after you have moved ImageBottom and then false again after you have moved ImageTop. But if dt is smaller than usual it could happen that ImageBottom is not moved sufficiently for the <= comparison to become true which means ImageTop won't get moved that time.

SubZeroWins wrote:
Below I added "MOVE BOTTOM" and "MOVE TOP" to show you precisely how the values and checks carry over. Proof that the dt and the distance to move is EXACTLY the same for both the top and bottom.

This proves my point.

First you have ImageBottom.left=1024.81 and ImageTop.left=1065.02:
At this time the <= expression would be
1024.81 <= 1065.02 - .4f * 150
=
1024.81 <= 1005.2
=
false

But then you update the position of ImageBottom so that ImageBottom.left=1004.87 and ImageTop.left=1065.02:
And now the <= expression would be
1004.87 <= 1065.02 - .4f * 150
=
1004.87 <= 1005.2
=
true

Since it is now true when the if statement is executed it goes ahead and moves ImageTop so that you have ImageBottom.left=1004.87 and ImageTop.left=1045.08:
Which brings you back to
1004.87 <= 1045.08 - .4f * 150
=
1004.87 <= 985.08
=
false

And then it continues like this...

... until ImageBottom.left=924.386 and ImageTop.left=964.602:
924.386 <= 964.602 - .4f * 150
=
924.386 <= 904.602
=
false

And after ImageBottom has been moved you get ImageBottom.left=904.859 and ImageTop.left=964.602:
904.859 <= 964.602 - .4f * 150
=
904.859 <= 904.602
=
false

It's still false (!) and that's is why ImageTop is NOT moved here.

Why did this happen here? Well, if you look at the dt value 0.016273 it's smaller than it has been before. It was just too small for ImageBottom to move enough for the <= if condition to become true. That's all there is to it.

SubZeroWins wrote:
You seem to be more certain that it is not loss of precision? Even the getGlobalBounds() rounds off to the 3rd decimal place and loses some value, although minor. Maybe that combined with some loss in the subtraction and checking?

I'm not saying it doesn't happen. It's just not "the problem".
You should always expect there to be loss of precision/rounding errors when working with floating-point numbers.
No matter how hard you try there will always be some of it.
A robust bug-free solution need to be written in a way that it behaves well even when things are a little off.

The solution to the problem is not to try and minimize rounding errors. Instead you need to change the way you handle the movement, because the "logic" is simply wrong.

I have already offered a possible solution in an earlier post. I don't know if you ever tried it. I haven't tested it so it's of course possible that I made some mistake or forgot to think about something.

In games I think it's common to move all objects first and then handle collisions afterwards. Not sure if it's helpful for what you're doing but perhaps something to consider if you're planning to have many objects moving around affecting each other.
Last edited on
I get what you are saying and it is true that when you have a small difference between the bottom left and the marker, and then a subsequent smaller dt, then the <= no longer becomes true for ONE FRAME and then catches up on the next. But your interpretation of the if()'s is off, as there is ONLY ONE single time the if is false so that the else runs for my last list of cout's...it is this one below. This is the only time it is false once it starts moving and then glitches, and other than at the very beginning of the program and for which I did not show in this small sample I posted last.

NOT*********************************************
dt = 0.016273
ImageTop marker if() check to move <= 904.602
ImageBottom.left = 904.859
ImageTop.left with no move = 964.602


That is the output of the else() from the if(<=) that goes FALSE.
904.859 <= 904.602
FALSE

All of the other "MOVE TOP" are printed in the if(<=) statement when it is true and they are most definitely always true and get printed exactly the way the program prints them. So the "MOVE TOP" can only be output when the if(<=) is true.


From the very top of my last list:

MOVE BOTTOM
dt = 0.016616
ImageBottom.left before move = 1024.81
ImageBottom.left after move = 1024.81 - 19.9392 = 1004.87


ImageBottom starts off at 1024.81, then it is moved left for 1 dt frame and the new left is 1004.87 before it enters the if() check below.


MOVE TOP
dt = 0.016616
ImageTop marker if() check to move <= 1005.02
ImageBottom.left = 1004.87
ImageTop.left before move = 1065.02
ImageTop.left after move = 1065.02 - 19.9392 = 1045.08


if (1004.87 <= 1005.02)
TRUE

Again, EVERY SINGLE "MOVE TOP" is true in this list and the only one that is false is the NOT*******.

From the very beginning of the program the outputs will look something like this, as you seem to have pointed out in one of your past posts and it is true:

MOVE BOTTOM
NOT*********************************************
MOVE BOTTOM
NOT*********************************************
MOVE BOTTOM
NOT*********************************************
......etc.....then when if(<=) is true it prints.......
MOVE BOTTOM
MOVE TOP
MOVE BOTTOM
MOVE TOP
MOVE BOTTOM
MOVE TOP
MOVE BOTTOM
..........etc...many times until the glitch happens and the if(<=) becomes false
for only 1 frame
NOT*********************************************
MOVE BOTTOM
MOVE TOP
MOVE BOTTOM
MOVE TOP
MOVE BOTTOM
MOVE TOP
MOVE BOTTOM
MOVE TOP
............never a false again, at least not for a long enough time that I have ever been able to try for.
Last edited on
SubZeroWins wrote:
But your interpretation of the if()'s is off, as there is ONLY ONE single time the if is false so that the else runs for my last list of cout's

I saw it was only once. I agree with everything you say in the rest of your post.

What I did was that I showed you what you would get if you had evaluated and printed the result of the <= expression at other times. The purpose was just to show what is normally happening and compare it to what happens when the "NOT" happens so that you should understand why it is happening.
Last edited on
I understand now, a printout at the end of each frame and it being mostly false. I will look at that and your change in if() at some point as well. Thanks.
SubZeroWins wrote:
I will look at that and your change in if() at some point as well.

If you're referring to what I wrote here ...
https://cplusplus.com/forum/beginner/284807/2/#msg1234738
... then I just want to make it clear that the important change is not in the if condition (it should be equivalent to what you had). The important change is in the code that updates the position (...std::max(targetX,...)...). After this change I think you should even be able to always run that code every frame without the if statement.

If it solves the "NOT glitch" (which I think it will) but you think ImageTop stops too early to the right, that is because now you are stopping exactly at the "targetX" position and not going past it like you did earlier, so to make it look right again you might have to adjust "targetX" so that it's a little further to the left.
Last edited on
Peter, you were right about the max() code below and there was no need to use a bool lock or check. It just never pops/jitters because it takes the max val.
1
2
3
4
5
float targetX = ImageBottom.getGlobalBounds().left + 0.1f * ImageTop.getGlobalBounds().width;
if (targetX <= ImageTop.getGlobalBounds().left)
{
	ImageTop.setPosition(std::max(targetX, ImageTop.getGlobalBounds().left - m_SpeedScalesMovingRow * dt.asSeconds()), ImageTop.getGlobalBounds().top);
}


Last time I was happy to just get it to work and not jitter but after stacking multiple images on top to see how it performed it made me realize that I can do better and refine it more. Not because I really need to but because I could and it is good practice.

So now what I do is to take the difference between the marker and the bottom image and then start moving the top image at a slower pace than the bottom. Once the <= is true then I move both images at the same top speed. I also feed back the difference between the marker and the bottom image into the speed of the top image to gradually and unnoticeable move them closer and more precisely to the .1 distance from the marker.

Before the distance between them can be <= but they were not exactly .1 apart and now I gradually bring it closer and closer.

I am curious about one other thing. When you setup a Text and draw it you do this.

1
2
Text textObj;
window.draw(textObj);


But how do you draw a vector<Text> m_Texts; that comes from a class?
From main I can access the individual Texts in the vector like this.

 
BigClass.m_Texts[0].getFont();


But I cannot draw it like that below without generating an Exception throw. I don't really need to use a vector but it would be nice to know. I think it has something to do with dereferencing.

 
window.draw(BigClass.m_Texts[0]);
I'm not sure what kind of "Exception" you're talking about.

If the problem is that you get an access violation/out of bounds error then it's probably because you're trying to access elements in the vector that don't exist. You probably want to use a loop if you want to display all texts, something like:
1
2
3
4
for (std::size_t i = 0; i < vecOfTexts.size(); ++i)
{
	window.draw(vecOfTexts[i]);
}
or
1
2
3
4
for (const sf::Text& text : vecOfTexts)
{
	window.draw(text);
}

If BigClass is the name of the class and m_Texts is a public static member then you need to write BigClass::m_Texts instead of BigClass.m_Texts.

If BigClass is the name of the class and m_Texts is a public non-static member then you need to use an instance (object) of the class to access the vector.
1
2
3
4
5
BigClass myBigClassObject;
for (const sf::Text& text : myBigClassObject.m_Texts)
{
	window.draw(text);
}

Although, normally I would expect member variables to be private (especially if they have an m_ prefix) which would mean you cannot access them from outside the class. Instead you might want to have a function (perhaps called "draw" or "drawTexts") that you can call to draw the texts.
1
2
BigClass myBigClassObject;
myBigClassObject.drawTexts(window);

Another approach is to have a member function that returns (a reference to) the vector of texts (though that will leak some implementation details).
1
2
3
4
5
6
BigClass myBigClassObject;
const std::vector<sf::Text>& texts = myBigClassObject.getTexts();
for (const sf::Text& text : texts)
{
	window.draw(text);
}


Note that sf::Text handles multiple lines just fine, so if you're using std::vector<sf::Text> to implement multi-line text you might want to consider using a single sf::Text and separate each line with a newline character '\n' instead.
Last edited on
Good info, thanks!

It is a public non-static member and I am already using a for loop to set all the text info and it works fine, so it is not out of range.

I thought about using a function to send a reference but it was too easy and I am only interested in this one because I had problems and would like to know for reference.

I tried your range-based for loop example and it still throws exception, then I decided to look at it closer. I had a feeling it might be the font that is passed to it and I almost changed it last time. I changed it now and the problem went away. I was setting

Font font2;

in the constructor. Which I guess is passing it as a reference and when the constructor terminators so does font2 (m_Texts[0].setFont(font2);). I moved it to the header file and now it works fine.

Last edited on
Registered users can post here. Sign in or register to post.
Pages: 123