One thing you should know about numbers in Arma is that they adhere to IEEE floating point standard. Go ahead and look it up on wikipedia for details, but in layman’s terms this means that there is a limit after which the numbers start losing their precision, among other things like -1 x 0 = – 0 (though  0 == – 0 returns true).

Anyway, if you want to display the number and see it with your own eyes you have to convert it to string first. The thing is, the automatic number to string conversion in Arma whether using str command or format is limited to 6 digits and this goes for both, floats and integers. So the smallest number you can display this way is -999999 while the biggest one is 999999. Anything above this threshold starts to get rounded: 9999999 => 1e+007

Because of that, the floats converted to string will drastically reduce precision the greater they are. 9.99999 has 5 digits after floating point while 99999.9 has only 1. Why is this a problem? The position coordinates of objects are in metres and they are floats too. If my X coordinate displays 1234.12, this means I’m one kilometre, two hundred and thirty four metres and twelve centimetres away from the map origin, which is [0,0,0].

In reality my position on the map is much more precise than just 2 digits after floating point. It could be 1234.123456 for what we know. But if I were to store it in a database, because database extension only accepts string, after default number to string conversion and rounding, 1234.12 is what will get stored. If I then want to restore position from the database, I can only restore it to the nearest centimetre. And the further away I’m from map origin the worse displayed precision. SaMatra even made some tests far away from the origin to see how this would affect models:

samatra

So here is the challenge, how to convert float to string without losing precision needed for the map placement? We know the floats stored as numbers preserve their precision. To check it I will store my X coordinate in a variable as a number and then subtract a default 6 digit rounded variant from it.

x = getPos player select 0; xStr = str x; //2475.53 precisionStr = str (x - parseNumber xStr); //-0.00170898

This means that if I add both values together I should theoretically restore my original position with acceptable precision. Let’s check this out:

x == parseNumber xStr //false x == (parseNumber xStr + parseNumber precisionStr) //true

It’s true! If I manually add both number together I will get 2475.52829102. Let’s do one more check:

x == 2475.52829102 //true

It works! I’ve even made a function earlier that would store coordinates in a database as an array of 2 strings, rounded and precision: [“2475.53″,”-0.00170898″] . All you need is to add them together after reading them from database to get your precise coordinate. But then HeliJunkie reminded me that I’m overlooking a very useful in this case % (modulo) command. Let’s see how the same works out with alternative method:

x = getPos player select 0; xStr = str (x - x % 1); //2475 precisionStr = str (x % 1); //0.52832

This gives us 2475.52832, which is little bit different from  2475.52829102. Will this be enough?

x == (parseNumber xStr + parseNumber precisionStr) //true x == 2475.52832 //true

Success! This also means that lower level of precision in this case is enough and that we can also just store this as one string instead of two with little effort. It’s time to wrap it all up into a nice function:

KK_fnc_floatToString = { private "_arr"; _arr = toArray str abs (_this % 1); _arr set [0, 32]; toString (toArray str ( abs (_this - _this % 1) * _this / abs _this ) + _arr - [32]) };

Let’s run a few tests to ensure this works as intended:

y = getPos player select 1; str y; //5656.81 yStr = y call KK_fnc_floatToString; //5656.807617 y == str y //false y == parseNumber yStr //true

So far so good, we now have a function to preserve float precision and store it in a string for later. Now it is time to construct another function that would convert passed position array to string for the use with database extension. Basically what we need is a string that looks like array, that we can simply call compile later to return the array:

KK_fnc_positionToString = { { _this = if (_forEachIndex == 0) then [ {_x call KK_fnc_floatToString}, {_this + "," + (_x call KK_fnc_floatToString)} ]; } forEach +_this; format ["[%1]", _this] };

This is what a sample output looks like:

pos = getPosASL player; str pos; /*[ 2497, 5659.18, 74.5057 ]*/ pos2str = pos call KK_fnc_positionToString; /*[ 2497.00244141, 5659.183594, 74.505684 ]*/

A final test to make sure everything works. This time we measure the distance between 2 positions. If the distance between the original position and stored position is 0 we are spot on.

pos distance call compile str pos; //0.00423813 pos distance call compile pos2str; //0

Sweet! Here you go, an essential set of functions for DB operations. I’m also not quite finished with Arma and numbers. I’m planning at least one more blog post about it, in the meantime…

Enjoy,
KK

EDIT: Decided to optimise KK_fnc_positionToString for speed and make it self-contained. The new variant is about 1.3 times faster:

KK_fnc_positionToString = { private ["_f2s","_arr"]; _f2s = { _arr = toArray str abs (_this % 1); _arr set [0, 32]; toString (toArray str ( abs (_this - _this % 1) * _this / abs _this ) + _arr - [32]) }; format [ "[%1,%2,%3]", _this select 0 call _f2s, _this select 1 call _f2s, _this select 2 call _f2s ] };

EDIT2: BUG FIX! Big thanks to Das Attorney for pointing out (below in comments) a bug with negative numbers. All fixed (_arr = toArray str (_this % 1); should be _arr = toArray str abs (_this % 1); of course 🙂 )

EDIT3: BUG FIX! Again, big thanks to Das Attorney for pointing out yet another bug with, abs!!! 🙂 )

EDIT4: Decided to rewrite both functions after some array commands have been expanded to include string functionality. New KK_fnc_positionToString execution time is 0.08ms vs 0.09ms of the old one. Not major, but still:

KK_fnc_floatToString = { private "_rem"; _rem = str (_this % 1); format [ "%1%2", if (_this < 0) then [ {ceil _this}, {floor _this} ], _rem select [_rem find "."] ] }; KK_fnc_positionToString = { private ["_fnc","_rem"]; _fnc = { _rem = str (_this % 1); format [ "%1%2", if (_this < 0) then [ {ceil _this}, {floor _this} ], _rem select [_rem find "."] ] }; format [ "[%1,%2,%3]", _this select 0 call _fnc, _this select 1 call _fnc, _this select 2 call _fnc ] };

EDIT5: ACE’s Nouber gave me an idea so I rewrote float to string a little bit and so far it is the fastest:

KKNou_fnc_floatToString = { if (_this < 0) then { str ceil _this + (str (_this - ceil _this) select [2]) } else { str floor _this + (str (_this - floor _this) select [1]) }; };

And here is position function based on the above solution. Use these two functions as they are faster and also do not fail on large numbers when engine starts rounding them:

KK_fnc_positionToString = { private "_fnc"; _fnc = { if (_this < 0) then { str ceil _this + (str (_this - ceil _this) select [2]) } else { str floor _this + (str (_this - floor _this) select [1]) }; }; format [ "[%1,%2,%3]", _this select 0 call _fnc, _this select 1 call _fnc, _this select 2 call _fnc ] };