2

I'm trying to implement basic 2D vector math functions for a game, in Java. They will be intensively used by the game, so I want them to be as fast as possible.

I started with integers as the vector coordinates because the game needs nothing more precise for the coordinates, but for all calculations I still would have to change to double vectors to get a clear result (eg. intersection between two lines).

Using doubles, there are rounding errors. I could simply ignore them and use something like

d1 - d2 <=  0.0001

to compare the values, but I assume with further calculations the error could sum up until it becomes significant. So I thought I could round them after every possibly unprecise operation, but that turned out to produce much worse results, assumedly because the program also rounds unexact values (eg. 0.33333333... -> 0.3333300...).

Using BigDecimal would be far too slow.

What is the best way to solve this problem?

7
  • If your game only has integer coordinates, you'll necessarily loose some precision at some point, no? But it should be clear that rounding without need is not a good way to improve the accuracy of your results. People usually just live with the inaccuracy of floating point and it works fine if you know what you are doing. What is the worst that could happen in your game if a number is a tiny little bit off?
    – 5gon12eder
    Commented Oct 16, 2015 at 15:26
  • for line intersections, such rounding errors are significant only for very small angles Commented Oct 16, 2015 at 15:28
  • 2
    How accurate is accurate enough? Do you really need this level of precision? Will players even notice it?
    – JonK
    Commented Oct 16, 2015 at 15:31
  • 4
    I would implement it as simply as possible, using double. The built-in rounding was carefully designed by experts on numerical approximation and rounding error. You are unlikely to be able to improve it by adding your own rounding. Commented Oct 16, 2015 at 15:36
  • 1
    Like you suggested, use an epsilon value that is an acceptable level of precision, e.g. 0.0001.
    – ryuu9187
    Commented Oct 16, 2015 at 15:41

2 Answers 2

5

Inaccurate Method

When you are using numbers that require Precise calculations you need to be sure that you aren't doing something like: (and this is what it seems like you are currently doing)

error accumulation

This will result in the accumulation of rounding errors as the process continues; giving you extremely innacurate data long-term. In the above example, you are actually rounding off the starting float 4 times, each time it becomes more and more inaccurate!


Accurate Method

A better and more accurate way of obtaining numbers is to do this: avoid accumulation of rounding errors

This will help you to avoid the accumulation of rounding errors because each calculation is based off of only 1 conversion and the results from that conversion are not compounded into the next calculation.

The best method of attack would be to start at the highest precision that is necessary, then convert on an as-needed basis, but leave the original intact. I would suggest you to follow the process from the second picture that I posted.

I started with integers as the vector coordinates because the game needs nothing more precise for the coordinates, but for all calculations I still would have to change to double vectors to get a clear result (eg. intersection between two lines).

It's important to note that you should not attempt to perform any type of rounding of your values if there is not noticeable impact on your end result; you will simply be doing more work for little to no gain, and may even suffer a performance decrease if done often enough.

1
  • 1
    Good answer. You usually distinguish between game state and visual representation. Game state are variables that are as precise as needed (they are the floats in this answer) and are updated every game tick based on their previous values (but not based on the display state). After this update, the other variables (visual representation, helper variabels) are derived using the game state. For the visual derivation, this is called rendering.
    – brimborium
    Commented Oct 16, 2015 at 16:39
2

This is a minor addition to the prior answer. When converting the float to an integer, it is important to round rather than just casting. In the following program, d is the largest double that is strictly less than 1.0. It could easily arise as the result of a calculation that would have result 1.0 in infinitely precise real number arithmetic.

The simple cast gets result 0. Rounding first gets result 1.

public class Test {
  public static void main(String[] args) {
    double d = Math.nextDown(1.0);
    System.out.println(d);
    System.out.println((int)d);
    System.out.println((int)Math.round(d));
  }
}

Output:

0.9999999999999999
0
1

Not the answer you're looking for? Browse other questions tagged or ask your own question.