float issues

Started by
16 comments, last by icecubeflower 14 years, 11 months ago
Hey I have a problem with floats I'm sure lots of people have had before. I guess it has to do with them only being able to represent a finite number of fractions between one whole number and the next. I gotta 2D Zelda-like game going on. Everyone has a float fsize. So they can have any kind of radius they want. The map is gridded with a 64x48 blocks per screen grid. So say a guy's x position was 622.99998596 and the amount he needed to move to the right was 1.856942. And he's a tiny little bug with a radius of 1.0. That means I consider him a circle and his right edge is at 623.99998596. The blocks are 16 pixels wide. x=624 is the left side of the next block. That would be block 39 in the horizontal sense. Every 1/10,000 checks or so screws up and a guy will walk over a block. I decide which is the next block to check horizontally, like this: int(fhor+fsize)/16+1 So int(622.99998596+1.0)/16+1 SHOULD equal 623/16+1=38+1=39 But it doesn't. In computer world 622.99998596+1.0=624.0 for some reason and I end up checking block 40 which is the wrong block and then the guy freely moves onto block 39. So.... what's the solution? All I can think of is to stop butting guys up so close to the next block, like maybe keep their edges a full 1.0f away from the next block or something. Any better ideas? [Edited by - icecubeflower on May 9, 2009 2:30:00 AM]
Advertisement
The value 622.99998596 is not representable exactly in limited floating point precision. In fact, the closest approximation that can be stored is exactly 623.0.

edit: Well, the solution; perhaps use double? Double can store that value exactly. But you can also argue; if placing something so close to the border that floating point precision is not sufficient to distinguish which tile it is, can it not be considered to be on both tiles at the same time (in the sense that whichever tile the precision results in, you use that tile)? After all, it is pretty much on the exact edge between them, leaving you with a corner case. Design your logic around this possibility. Or, as you said, just don't put something so close to the edge.
Use double and it will work.
The simple solution would be to do two checks. For the first check, round the value down. For the second check, round it up. If these correspond to different tiles, you are on an edge, and you should respond accordingly.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Quote:Original post by ApochPiQ
The simple solution would be to do two checks. For the first check, round the value down. For the second check, round it up. If these correspond to different tiles, you are on an edge, and you should respond accordingly.


That won't help him if he sticks with float, there are just not enough bytes to store this number:

float number_f = 622.99998596;double number_d = 622.99998596;	printf("%lf\n", number_f);printf("%lf\n", number_d);


Output is:

623.000000622.999986
You could always just try adding 0.5 instead of 1.0. This way it won't tip it over to the next number unless it was already going to round up to there anyway. Or as everyone else said, just use a double for the accuracy.
Or you could do away with floats all together and avoid this whole accuracy issue. Use a 28.4 or 24.8 fixed-point format and be done with it, it seems rather silly and wasteful to be using floating-point coordinates in a world represented by edges on an integer grid.

throw table_exception("(? ???)? ? ???");

I don't see how using a double would change anything. That number, 622.99998596, isn't even important. When a cycle happens the time passed since last cycle is multiplied by the character's speed and he is moved a certain amount. If I change to double then with the extra precision my characters will just have to happen to stop a little bit closer to the edge and I will have the exact same problem.

I don't know what a 24.8 fixed format is.

I thought I had to use floats (or doubles or whatever) because I have framerate independent movement. The world is represented by an integer grid but if I move my characters by integers the movement would get choppy. I resolve the float coordinates to integer coordinates when I draw.
Quote:Original post by icecubeflower
I don't know what a 24.8 fixed format is.

It means you use 24 bits to represent the whole part of a value, and 8 bits to represent the fractional part. It makes more sense since you have a world that is represented by a uniform grid, and fixed-point formats have uniform precision. Therefore you have the same amount of precision everywhere in the world and there aren't any strange bugs that appear when you're far away from the origin.

You could also use integers if you're not comfortable with fixed-point and say that every tile is 256x256 units large, and a coordinate like 346x130 means you're inside tile 1x0, 90 units from the left edge and 130 units from the top edge.
Discarding the fractional part of fhor is the real problem here. 623.99998596 tells you that you are right on the edge of block 38 and 39 (more importantly, 624.0 also tells you the same), whereas int(623.99998596) tells you that you are somewhere in block 38 OR (more rarely) somewhere in block 39. Thus the problem is not so much with floating point numbers, but in the discarding of valuable information.

Fixed precision would solve the problem nicely, but for floating precision, my approach would be to treat both blocks and characters as an interval.

Given your example:

x position = 622.99998596
x movement = 1.856942
radius = 1.0

The player is attempting to move to 624.85692796, with radius 1.0, or the range 623.85692796 to 625.85692796.

We then look up the tiles in this range:
int(623.85692796 / 16) = 38
int(625.85692796 / 16) = 39

If either tile 38 or 39 are solid, the simplest response is to not move at all on that axis, but this can be refined, for example, to project the player out of the tile.

This topic is closed to new replies.

Advertisement