6

I'm developing a 3D space game, which is using alot of math formulas, navigation, ease effects, rotations, huge distances between planets, objects mass, and so on...

My Question is what would be the best way in doing so using math. Should I calculate everything as integers and obtain really large integers(over 20 digits), or use small numbers with decimals.

In my experience, math when using digits with decimals is not accurate, causing strange behavior when using large numbers with decimals.

user5680735
  • 703
  • 2
  • 7
  • 21
Rotar Paul
  • 129
  • 2
  • 13
  • That true of almost all programming languages, it is not necessarily easy to represent arbitrary precision floating point numbers in binary. For a good layout of the issues see http://blog.aventine.se/2014/03/09/a-silly-review-of-dec64.html – Jared Smith Jun 16 '16 at 21:07
  • Yeah, I wouldn't recommend doing any sort of math in Javascript using decimals. Javascript is notoriously terrible at decimal math and you WILL get painful rounding errors all over the place. – jaybee Jun 16 '16 at 21:27
  • 3
    The 17 digits of precision you get from JavaScript's 64-bit floating point numbers is enough for most applications. Not sure what you refer to with "not accurate." – Joni Jun 16 '16 at 21:34
  • 2
    Wait, when you say over 20 digits are you referring to over 20 significant digits? – Joseph Casey Jun 16 '16 at 21:47
  • 1
    Are you sure your math is correct? I had some pretty strange results with a small gravity simulation (looked "almost" correct, but earth was going into an elliptical orbit quickly) -- in the end it was just a bug in my code... https://tidejnet.appspot.com/v3/#id=1pgjxn6691lad If you simulate really big systems, you may want to use a hierarchical reference system to avoid mixing very big and very small numbers... – Stefan Haustein Jun 16 '16 at 22:23

4 Answers4

4

I would avoid using decimals. They have known issues with precision: http://floating-point-gui.de/


I would recommend using integers, though if you need to work with very large integers I would suggest using a big number or big integer library such as one of these:

The downside is you have to use these number objects and their methods rather than the primitive Number type and standard JS operators, but you'll have a lot more flexibility with operating on large numbers.

Edit:
As le_m pointed out, another downside is speed. The library methods won't run as fast as the native operators. You'll have to test for yourself to see if the performance is acceptable.

Community
  • 1
  • 1
skyline3000
  • 7,639
  • 2
  • 24
  • 33
2

The distance from our Sun to Alpha Centauri is 4.153×1018 cm. You can represent this value well with the Number datatype which stores values up to 1.7977×10308 with about 17 significant figures.

However, what if you want to model a spaceship stationed at Alpha Centauri?

Due to the limited precision of Number, you can either store the value 4153000000000000000 or 4153000000000000500, but nothing in between. This means that you would have a maximal spacial resolution of 500 cm at Alpha Centauri. Your spaceship would look really clunky.

Could we use another datatype than Number? Of course you could use a library such as BigNumber.js which provides support for nearly unlimited precision. You can park your spaceship one milimeter next to the hot core of Alpha Centauri without (numerical) issues:

pos_acentauri = new BigNumber(4153000000000000000);

pos_spaceship = pos_acentauri.add(0.1); // one milimeter from Alpha Centauri

console.log(pos_spaceship); // 4153000000000000000.1
<script src="https://cdnjs.cloudflare.com/ajax/libs/bignumber.js/2.3.0/bignumber.min.js"></script>

However, not only would the captain of that ship burn to death, but your 3D engine would be slow as hell, too. That is because Number allows for really fast arithmetic computations in constant time, whereas e. g. the BigNumber addition computation time grows with the size of the stored value.

Solution: Use Number for your 3D engine. You could use different local coordinate systems, e. g. one for Alpha Centauri and one for our solar system. Only use BigNumber for things like the HUD, game stats and so on.

le_m
  • 19,302
  • 9
  • 64
  • 74
  • Using relative coordinate systems seems like the right choice here. – Stefan Haustein Jun 16 '16 at 22:18
  • Well basically each star system will start at XYZ = 0,0,0. The math will only need to be applied in each system separatelly, so the distance between Alpha Centauri and Sol will not be considered at this point since I only need to calculate the interplanetary distance. When a player goes interstellar, I'll get the proper data from the database and recreate the 3D scene with the response from the query – Rotar Paul Jun 17 '16 at 11:19
  • So everything is fine then? Distance to Pluto is 590637627200000 cm, you still have milimeter precision there.Or do you need to model every micrometer between Sun and Pluto? Still possible when you run your simulation on BigNumber. Your 3D coordinate system used for rendering would still use `Number` and probably have the cockpit as orgin (0,0,0). Depends on you game, of course. – le_m Jun 17 '16 at 12:18
  • I don't need micrometric precission, but metter precission. Using decimals made everything go really weird once the ship was reaching large numbers e.g 243565.00293. Integers seems to do it just fine, and saved my day :) – Rotar Paul Jun 18 '16 at 10:21
1

Use the JavaScript Number Object

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number

Number.MAX_SAFE_INTEGER The maximum safe integer in JavaScript (2^53 - 1).

Number.MIN_SAFE_INTEGER The minimum safe integer in JavaScript (-(253 - 1)).

var biggestInt = 9007199254740992;
var smallestInt = -9007199254740992;

var biggestNum = Number.MAX_VALUE;
var smallestNum = Number.MIN_VALUE;
var infiniteNum = Number.POSITIVE_INFINITY;
var negInfiniteNum = Number.NEGATIVE_INFINITY;
var notANum = Number.NaN;

console.log(biggestInt); // http://www.miniwebtool.com/scientific-notation-to-decimal-converter/?a=1.79769313&b=308
console.log(smallestInt); // http://www.miniwebtool.com/scientific-notation-to-decimal-converter/?a=5&b=-32

console.log(biggestNum);
console.log(smallestNum);
console.log(infiniteNum);
console.log(negInfiniteNum);
console.log(notANum);

debugger;

I can only imagine that this is a sign of a bigger problem with your application complicating something that could be very simple.


Please read numerical literals

http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.3

Once the exact MV for a numeric literal has been determined, it is then rounded to a value of the Number type. If the MV is 0, then the rounded value is +0; otherwise, the rounded value must be the Number value for the MV (as specified in 8.5), unless the literal is a DecimalLiteral and the literal has more than 20 significant digits, in which case the Number value may be either the Number value for the MV of a literal produced by replacing each significant digit after the 20th with a 0 digit or the Number value for the MV of a literal produced by replacing each significant digit after the 20th with a 0 digit and then incrementing the literal at the 20th significant digit position. A digit is significant if it is not part of an ExponentPart and

  • it is not 0;
  • or there is a nonzero digit to its left and there is a nonzero digit, not in the ExponentPart, to its right.

Clarification

I should add that the Number Object wrapper supposedly offers precision to 100 (Going above this number will give you a RangeType error) significant digits in some browsers, however most environments currently only implement the precision to the required 21 significant digits.

Reading through OPs original question, I believe skyline provided the best answer by recommending a library which offers well over 100 significant digits (some of the tests that I got to pass were using 250 significant digits). In reality, it would be interesting to see someone revive one of those projects again.

Joseph Casey
  • 1,283
  • 1
  • 14
  • 34
0

The problem with BigNumber is with

Precision loss from using numeric literals with more than 15 significant digits

My solution would be a combination of BigNumber and web3.js:

var web3 = new Web3();
let one = new BigNumber("1234567890121234567890123456789012345");
let two =   new BigNumber("1000000000000000000");
let three = new BigNumber("1000000000000000000");
const minus = two.times(three).minus(one);
const plus = one.plus(two.times(three));
const compare = minus.comparedTo(plus);
const results = {
    minus: web3.toBigNumber(minus).toString(10),
    plus: web3.toBigNumber(plus).toString(10),
    compare
}
console.log(results); // {minus: "-234567890121234567890123456789012345", plus: "2234567890121234567890123456789012345", compare: -1}
Tudor Morar
  • 3,720
  • 2
  • 27
  • 25