Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

For double f, float g, How to find largest int i, such that i*g <= f

Tags:

java

UPDATE: Thanks Kevin, I meant <= in the title, not <.

Is this correct? It's the best I've managed after an embarrassing amount of time on the problem:

public int candidate_answer(double f, float g) {
    int test = (int)Math.floor(f / g);
    if ((test + 1) * g <= f) {
        test++;
    }
    return test;
}

Background:

The application is a simple game that I've taken over ownership for from a previous programmer. Curiously, he has chosen to mix floats and doubles wantonly, both in member and argument variables, so there's a lot of unnecessary implicit and explicit casting going up and down.

The player coordinates are at double x, y (the player is assumed to be a point). There is a float TILE_SIZE, and the world is some number of rows and columns worth of tiles, plus some generic out-of-bounds handling. Assuming the (x,y) coordinate is in bounds, I'm trying to figure out which tile the user is in, based on x (to get column) or y (to get row). This is like, programmer 101.

WLOG, At first I was just doing col = (int)(x/TILE_SIZE). But as I discovered, e.g., .5/.1f < 5, and thus (int)(.5/.1f) == 4, the wrong answer. This led to the above if statement and formulation of the problem.

Then I discovered that, e.g., (int)(-9.999999747378752E-5 / .1f) == 0, which led me to call Math.floor first.

But now I'm not sure what other bugs lurk in this approach, or what the best approach would be.

(It may not seem like such a big deal if the user is right on the cusp of being in one row or another, and we accidentally round to the wrong one; but the real issue is in the code, where we see unexpected sign changes (+, -, 0). For instance, some of the code assumes that if the user is on the tile at (r,c), then it's point is actually contained by that tile geometry. When it's not, we get things like negative distances when only non-negative are expected, and etc., and it causes asserts to fire and while loops to break and so on.)

like image 705
jdowdell Avatar asked Jul 07 '13 17:07

jdowdell


2 Answers

I think you should try to fix the root problem (.1f too far away from .1), i.e. turn TILE_SIZE into a double, or use floats consistently everywhere. Otherwise you might get similar problems caused by minimal rounding errors throughout the code. The candidate_answer function is a hack to work around a problem that should not exist in the first place.

P.S.

For this particular problem, you might just want to find a formula that is more robust, i.e. isn't sensitive to minimal rounding errors in one direction only. Try to put the player in the center of its field instead of at the tile edge where it can easily fall into rounding errors:

col = (int)((x + MINIMAL_STEP_SIZE / 2.0) / TILE_SIZE)

(Ideally, the MINIMAL_STEP_SIZE/2 part would already be part of x instead of being added here)

like image 188
Stefan Haustein Avatar answered Nov 02 '22 07:11

Stefan Haustein


Using double's all the way seems to be the way to go. However, this does not solve the root problem: Using floating point arithmetic is always bound to rounding errors.

When using floating point arithmetic, it is always good to work with an Epsilon (e) of accuracy:

If you want to check if x is equal to y (where one variable is a floating point type), use the following pattern:

if(x-y < e){
  ...
}

Epsilon must be defined (globally or at your opinion for a certain domain) based upon your requirements. For example:

e = 0.00001d;
like image 45
IsNull Avatar answered Nov 02 '22 06:11

IsNull