Float miscalculations

I'm trying to write an OpenGL program to demonstrate simple physics but I'm experiencing float miscalculations.

According to the Visual Studio debugger, when I add 0.001f to 0.0f I get 0.00095000007f.

This is very problematic because my "force" variable is incremented and decremented by floats, and in my program I check whether or not the force variable is exactly over or under 0.0f, and if it is, decrease the momentum further. This means that my object slows down but never truly stops.

How can I in C++ make it so that when I add 0.001f to 0.0f it does actually result in exactly 0.001f?

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#include <Windows.h>
#include <string>
#include <iostream>
#include <gl\freeglut.h>
#include <gl\GL.h>
#include <gl\GLU.h>

int keys[5];
float forcex = 0.0f;	// 0.0f = no force, 1.0f = full force, -1.0f = reverse force
float forcey = 0.0f;

#define UP 0			// Constants for key array
#define LEFT 1
#define DOWN 2
#define RIGHT 3
#define STOP 4

void setup(void);								// Set up OpenGL
void display(void);								// OpenGL display function
void logic(int value);							// Logic function
void physics(int value);						// Physics function
void keydown(unsigned char key, int x, int y);	// Keydown function
void keyup(unsigned char key, int x, int y);	// Keyup function

struct __Bounds
{
	int Left;
	int Right;
	int Up;
	int Down;
};

class __Square
{
public:
	__Bounds Bounds;
	__Square()
	{
		Bounds.Left = 0;
		Bounds.Right = 20;
		Bounds.Up = 0;
		Bounds.Down = 20;
	}
};

__Square Square;

int main (int argc, char *argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
	glutInitWindowSize(400, 400);
	glutCreateWindow("Game Engine");
	setup();
	glutDisplayFunc(display);
	glutTimerFunc(1000/100, logic, 1);
	glutTimerFunc(1000/100, physics, 2);
	glutKeyboardFunc(keydown);
	glutKeyboardUpFunc(keyup);
	glutMainLoop();
	return 0;
}

void setup(void)
{
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
}

void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glColor3f(0.0f, 1.0f, 0.0f);
	glRasterPos2f(0.0f, 0.0f);
	glRectf((GLfloat)Square.Bounds.Left/400, (GLfloat)Square.Bounds.Up/400, (GLfloat)Square.Bounds.Right/400, (GLfloat)Square.Bounds.Down/400);
	glutSwapBuffers();
}

void logic(int value)
{
	if(keys[UP] == 1)
	{
		forcey += 0.001f;
	}
	else if(keys[LEFT] == 1)
	{
		forcex -= 0.001f;
	}
	else if(keys[DOWN] == 1)
	{
		forcey -= 0.001f;
	}
	else if(keys[RIGHT] == 1)
	{
		forcex += 0.001f;
	}
	else if(keys[STOP] == 1)
	{
		forcex = 0.0f;
		forcey = 0.0f;
	}
	glutTimerFunc(1000/100, logic, 1);
}

void physics(int value)
{
	if(forcex > 0.0f)
	{
		forcex -= 0.00005f;
	}
	else if(forcex < 0.0f)
	{
		forcex += 0.00005f;
	}

	if(forcey > 0.0f)
	{
		forcey -= 0.00005f;
	}
	else if(forcey < 0.0f)
	{
		forcey += 0.00005f;
	}
	glTranslatef(forcex, forcey, 0.0f);
	glutPostRedisplay();
	glutTimerFunc(1000/100, physics, 2);
}

void keydown(unsigned char key, int x, int y)
{
	switch(key)
	{
	case 'w':
		{
			keys[UP] = 1;
		} break;
	case 'a':
		{
			keys[LEFT] = 1;
		} break;
	case 's':
		{
			keys[DOWN] = 1;
		} break;
	case 'd':
		{
			keys[RIGHT] = 1;
		} break;
	case 'e':
		{
			keys[STOP] = 1;
		} break;
	}
}

void keyup(unsigned char key, int x, int y)
{
	switch(key)
	{
	case 'w':
		{
			keys[UP] = 0;
		} break;
	case 'a':
		{
			keys[LEFT] = 0;
		} break;
	case 's':
		{
			keys[DOWN] = 0;
		} break;
	case 'd':
		{
			keys[RIGHT] = 0;
		} break;
	case 'e':
		{
			keys[STOP] = 0;
		} break;
	}
}

Last edited on
you can't...floating point values have high precision, meaning they can represent a huge range of values, but are not accurate. You should never directly use the == operator on a float as two values which 'should' be identical won't necessarily be. If you have an int on the other hand it has a fixed range of values, it can only represent whole numbers, so it has low precision, but has absolute accuracy meaning that a value of 1 is always exactly 1 and 0 is always exactly 0 and this applies to all numbers. This is a problem that floats have and there's no real way around it.

When comparing floats you should use your own function which returns true if the difference between the two floats is less than a given value
Could I just round the float?
what's your reasoning for rounding off? This is just decreasing the accuracy further surely
Well:

"According to the Visual Studio debugger, when I add 0.001f to 0.0f I get 0.00095000007f."

I want it to be 0.001f when I add 0.001f to 0.0f, so if I round 0.00095000007f I'd eventually get there, no?
Think about the size of the difference between 0.00095000007f and 0.001f. It is so small that I doubt it will make any difference to your application, the only problem you have here is comparing two floats for which you should make your own function.
Alright.

But how will I know the sort of range of inaccuracy float calculations can suffer from?

You may want to look up the IEEE 754 floating point standard for more information. The problem here is that most numbers do not have an exact representation in a limited size value. This is just like what happens when you try to represent 1/3 in base ten = 0.3333... The value can not be represented completely accurately since it infinitely repeats.


"According to the Visual Studio debugger, when I add 0.001f to 0.0f I get 0.00095000007f."


Floats only have something like 24 bits of precision, so anything that requires more than that will not be accurately represented. You can't fix this by rounding; the value you have above is probably as accurate as it can be given 32-bit floating point precision.

What does come out even is anything that has a representation that fits within the number of bits of precision. For base ten, the first digit after the decimal point is tenths, the second is hundredths, the third is thousandths, etc. Therefore, anything that can be represented as: (** represent exponentiation)

x*10**-1 + y*10**-2 + z*10**-3

will be accurately represented with 3 digits after the decimal point. Floating point values are stored in binary format, with binary exponents, so it changes to:

x*2**-1 + y*2**-2 + z*2** -3 which is equivalent to
x * 1/2 + y * 1/4 + z * 1/8

Because of this, 0.001 =1/1000 = 1/(2*2*2*5*5*5) decimal will not have an exact representation in binary. Only values that can be decomposed as halves, fourths, and eights will be represented accurately with 3 digits after the binary point.

http://en.wikipedia.org/wiki/IEEE_754-1985
http://www.cplusplus.com/reference/clibrary/cfloat/
(Look at your float.h header for system specific values)

I haven't worked with floats much, so I don't know if comparing to be within one FLT_EPSILON would work for you. It is defined as 1.19209290e-07F on my system.
Use doubles. or just accept that floats are not accurate.
doubles aren't accurate either.
I'm not sure if it will solve your problem, but try to replace float with GLfloat.
Topic archived. No new replies allowed.