Java2d time-based drive

Asked

Viewed 104 times

3

I’m trying to move an object using Java2d but I’m having some problems, I have following method:

public void logics() throws Exception{
    before = System.nanoTime();
    loop.logics(diff);
    after = System.nanoTime();
    diff = after - before;
}

Within the method loop.logics(long delta) I have the following code:

double ddelta = delta/1000000000.0;
x += speed*ddelta;// delta é a variação em segundos
System.out.println(x);

x and speed start at 100, however the result I see on the output is:

...
101.01591999999998
101.02638559999998
101.03550849999998
101.04340709999998
101.06836669999998
101.13834839999998
101.14830059999998
101.15935859999999
101.16879739999999
101.18246199999999
101.19245369999999
101.20157659999998
...

Giving a print on delta You can see she’s really short:

83330
80961
73063
80566
....

The intention is to make the x range of speed units per second. In a second, the sum of all the delta'obtained should be 1e9, thus the sum of all increments in x must be from speed, but as I have shown is not what happens, if anyone can help me I appreciate.

2 answers

1

Seems to me you’re running into two problems.

The first problem is that although you are using System.nanoTime(), the clock of your computer and/or operating system shall not have sufficient accuracy/resolution to measure the time in nanoseconds. In fact, as the javadocs of this method show:

This method provides nanosecond Precision, but not necessarily nanosecond Resolution (that is, how Frequently the value changes) - no guarantees are made except that the Resolution is at least as good as that of currentTimeMillis().

What translating into Portuguese is:

This method provides an accuracy of nanoseconds, but not necessarily a resolution of nanoseconds (i.e., how often the value changes) - no guarantee is made except that the resolution will be at least as good as that of currentTimeMillis().

The second problem I see is that you do calculations that can lose accuracy with the double. The double represents a number with a finite precision, since it occupies only 64 bits, varying the position of the decimal point. This means that small numbers can lose bits of information/accuracy when they are summed with large numbers. For example:

public class Calculo {
    public static void main(String[] args) {
        double x = 10 * 1e9;
        double y = 10 * 1e-9;
        System.out.println(x);
        System.out.println(x + y);
        System.out.println(x == x + y);
        double a = 10 * 1e7;
        double b = 10 * 1.557e-8;
        System.out.println(a + b);
    }
}

Here’s the way out:

1.0E10
1.0E10
true
1.0000000000000015E8

This shows that in this case, the small number (y) ended up being totally despised when it was added to the large number (x). This is due to the way the double round their values so that they fit in their representation. In the case of the a + b, some bits of the b They were cut so that they could be added together. In this way, what is happening in your code is that by adding several values to many decimal places (actually binary ones) of precision, the least significant bits end up being ignored so that the value fits within the 64 bits of the double.

Finally, the fact that you are using (double)diff/1000000000.0 makes you win the double rounding problems. Best would be to work with longs representing values in nanoseconds and only convert them in seconds when showing them somewhere.

Also, the fact that your method is loop.logics(float delta) instead of loop.logics(double delta) makes me suspect that there must be other places where your program suffers from rounding problems when converting from double (with 64 bits of information) for float (32 bits of information). In fact, the compiler was not supposed to give you an error about possible loss of accuracy when passing a double for a method whose parameter is float?

  • In relation to the second problem, the function signature was float, but in the tests I did while writing the question I switched to double, I’m going to change all the parameters to long, so each method converts accordingly, in which case I should be working in milliseconds? since the problem is with the computer.

  • You may prefer to use milliseconds, but I see no reason to use Milis instead of nanos.

1


I made some changes to the loop: The first big mistake is that I was ignoring the design phase to do the calculations (occurs shortly after the call to logics()), which is a big mistake since the rendering part takes a lot of time, the second was the way I calculated diff, the most accurate way to do this would be right before the call of the Logics(long delta) method, before also calculating the before, thus the method logics() gets like this:

@Override
public void logics() throws Exception{
    after =  System.nanoTime();
    diff = after - before;
    before = System.nanoTime();
    loop.logics(diff);
}

With these changes the drive is really good: using different values of speed: 1, 10 and 100 from to see that the variation of x is close to expected: increases approximately speed units per second.

  • Hello! Please make sure your intention is really to add an answer to your own question rather than accepting Victor’s solution. I understand from your answer that the problem was not loss of precision - I am correct?

  • He said that (double)diff/1000000000.0 made me win the conversion problems of double, did not get to check, just followed his tip and used long, leaving the conversion only to necessary situations, however this did not solve the problem, the situation here this complicated, the answer is based on something that I don’t include in the question (the rendering step), so you couldn’t detect the problem, but regardless of this it is the correct answer.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.