2

I am trying to write game with a scientifically accurate solar system, and am trying to figure out the best way to use coordinates in a massive scale. When I read about the coordinate system in Libgdx it said it uses "Floating Point" to store the coordinate data.

I want to have centimeter accuracy going out to ~5.5 billion kilometers, that is 12 decimal places. I am afraid that the coordinates won't be accurate when this large.

Can someone shed some light on this?

  • All of the Libgdx APIs I looked at use `float` rather than `double`. – Stephen C Aug 31 '17 at 22:45
  • @StephenC Would it be able to accurately store 12 decimal places? – Sean McKenna Aug 31 '17 at 23:23
  • If the APIs only support `float` then it is moot whether they "store" things with a greater precision. (You do know what the precision of the Java `float` type is ... I assume. If not, you should do some self-learning before you leap into this project.) – Stephen C Aug 31 '17 at 23:27
  • Are you sure about the 12 decimal places? 1 billion km = 10^9 km = 10^12 m = 10^14 cm – Socowi Sep 01 '17 at 00:11

3 Answers3

2

I never used the libgdx library, but looking at its API (for instance Vector3 or Rectangle), it seems it uses floats.

Floats

Java's float represent numbers in binary scientific notation, that is, each number is represented by a 23-bit significand and an 8-bit exponent. Think of it as a form (like a transfer form at your bank, where you have to write how much money you would like to transfer). In each box you can write a 1 or 0: float form

Note: 23 + 8 = 31, the missing bit is used as the sign of the number. In this answer, we do not deal with negative numbers or negative exponents.

Precision

Due to the exponent, floats get less precise for bigger numbers. Some examples:

 10_000_000 == 1E7 != 1E7 + 1 == 1.0000001E7 ==  10_000_001
100_000_000 == 1E8 == 1E8 + 1 == 1.0000000E8 == 100_000_000 

At 108 the significand is not precise enough to represent a difference of 1.

Lets calculate the precision for your use case, where numbers don't get bigger than 5.5 billion kilometers measured in centimeters:

5.5 × 109 km = 5.5 × 1014 cm = 1.11110100001110001101101 010100000011bin × 248 cm

Since we have only 23 bits to represent that number, it is approximated as

1.11110100001110001101101bin × 248 cm = 549'999'989'489'664 cm
= 5'499'999'894'896.64 m = 5'499'999'894.896 64 km

You would be off by more than 100 km!

Possible Workarounds

  • Use another library that uses double instead of float. Doubles have a 52-bit significand, which is precise enough for your use case.
  • Use two floats for each position: A coarse position and a fine-grained offset (not recommended).
  • Don't try to be that precise. I doubt that anyone can track objects in the outer solar systems centimeter-level precise.
Socowi
  • 17,678
  • 2
  • 21
  • 39
1

Ideally you want to create your own version of a Vector which uses BigDecimal since you can control the number of significands. The value of the number represented by the BigDecimal is therefore (unscaledValue × 10-scale), see Javadoc.

bitbrain
  • 549
  • 3
  • 11
1

It's good question to pops up interesting solution.

Your question tagged as libgdx, so my solution won't stay away from using it. Let's tackle into each issue here.

No Matter, It's Still Float

Firstly, I need to tell you that no matter how precision of position (float, double) you have, libgdx's underlying system will go with float at the end. See its SpriteBatch class which use float to represent position whenever it needs to draw something on screen, and also Sprite class which bases things off float for its position as well.

This means no matter your precision, things will be drew on screen in float.

Precision

Also no matter you use float or double, you will be getting floating-point calculation error. If you cannot be off by one bit (i don't know your game requirement deeply), whether it's about position or something else according to your game, then you might need to consider using BigDecimal. Bank application would use something like this as it cannot lose any one bit due to rounding-error, customer would likely complain!

You might get away by maintaining BigDecimal inside your game object. You update its position through them instead of normal way to set position in libgdx. But whenever you need to draw, you get its value via doubleValue(). Why double and not float? It's better to get highest precision that it can offers to use for your game, so when floating-number cannot 100% accurately represents the number, you will get smallest error as less as possible.

BigDecimal Is Slow

Be careful as BigDecimal is slower than normal floating-point operation. See its benchmarks here and here.

Workarounds

Because I see that at one point in time, users only see a limited area on your space. Or even if you might have a space map that allows user to zoom-out to see the whole area, that's a time when you don't really need precision or per-se double-precision, default way libgdx does is suffice and no need to switch game library (others will mostly base on float anyway) as well.

That's the reason that you can still use float but use BigDecimal or just String to represent any large scientific value on screen which doesn't involve in calculation that affects the game performance in run-time. Thus I believe this allows us to scale down the massive distance so you can represent game objects with same intention to be scientific correct.

haxpor
  • 1,871
  • 2
  • 24
  • 42