As promised, I’m making another post about numbers. What if you wanted to include currency transactions in your mission? As we have established already you would have trouble dealing with numbers over 1 million. For a currency emulation, this ceiling is way too low. One way to overcome this limitation is to deal with numbers in string format. This means to add and subtract them as you would do with pen and paper. Easy said than done?
Yesterday, I wrote this Arithmetic Library that gives the user 4 basic functions: add, subtract and compare of 2 numbers as well as format function to make a number look like currency. I haven’t written any multiplication or division functions simply because this is a simple library to allow accumulate capital, take care of purchase transactions and compare balances. It can even deal with overdrafts (negative numbers). I’m just going to dump the whole bunch of code here and show a few examples of use.
num_prep_ = {
private [
"_arr0","_arr1","_sgn0","_flp0","_sgn1","_flp1","_off",
"_arr","_max"
];
_arr0 = toArray (_this select 0) - [44];
_arr1 = toArray (_this select 1) - [44];
_sgn0 = if (_arr0 select 0 == 45) then {
_arr0 set [0, 'x'];
_arr0 = _arr0 - ['x'];
-1
} else {1};
_arr0 = [48] + _arr0;
_flp0 = _arr0 find 46;
if (_flp0 < 0) then {_flp0 = count _arr0};
_sgn1 = if (_arr1 select 0 == 45) then {
_arr1 set [0, 'x'];
_arr1 = _arr1 - ['x'];
-1
} else {1};
_arr1 = [48] + _arr1;
_flp1 = _arr1 find 46;
if (_flp1 < 0) then {_flp1 = count _arr1};
_off = _flp0 - _flp1;
if (_off != 0) then {
_arr = [];
_arr resize abs _off;
if (_off < 0) exitWith {_arr0 = _arr + _arr0};
if (_off > 0) exitWith {_arr1 = _arr + _arr1};
};
_max = count _arr0 max count _arr1;
_arr0 resize _max; _arr1 resize _max;
[_arr0, _arr1, _sgn0, _sgn1, _flp0 max _flp1, _max]
};
max_abs_ = {
private ["_sgn0","_sgn1","_max","_n0","_n1"];
_sgn0 = _this select 2;
_sgn1 = _this select 3;
_this set [6, -1 max (_sgn0 - _sgn1) min 1];
for "_i" from 0 to (_this select 5) - 1 do {
_n0 = _this select 0 select _i;
_n0 = if (isNil "_n0") then [{0},{parseNumber toString [_n0]}];
_n1 = _this select 1 select _i;
_n1 = if (isNil "_n1") then [{0},{parseNumber toString [_n1]}];
if (_n0 > _n1) exitWith {
_this set [6, -1 max (_sgn0 * 2 + _sgn1) min 1]
};
if (_n1 > _n0) exitWith {
_this = [
_this select 1, _this select 0, _sgn1, _sgn0,
_this select 4, _this select 5,
-(-1 max (_sgn0 + _sgn1 * 2) min 1)
];
};
};
_this
};
num_op_ = {
private ["_c","_n","_num","_n0","_n1"];
_c = 0; _n = 0; _num = "";
for "_i" from (_this select 5) - 1 to 0 step -1 do {
_num = (if (_i == (_this select 4)) then {"."} else {
_n0 = _this select 0 select _i;
_n0 = if (isNil "_n0") then [{0},{parseNumber toString [_n0]}];
_n1 = _this select 1 select _i;
_n1 = if (isNil "_n1") then [{0},{parseNumber toString [_n1]}];
if (_this select 7) then {
_n = _n0 + _n1 + _c;
_c = if (_n > 9) then [{_n = _n - 10; 1},{0}];
} else {
_n = _n0 - _n1 - _c;
_c = if (_n < 0) then [{_n = _n + 10; 1},{0}];
};
str _n
}) + _num;
};
_num
};
add_prepd_ = {
private "_res";
switch ((_this select 2) + (_this select 3)) do {
case (-2): {
_this set [7, true];
_res = (_this call num_op_) call trunk_zero_;
if (_res != "0") then {
_res = "-" + _res;
};
_res
};
case (0): {
_this = +_this call max_abs_;
_this set [7, false];
_res = (_this call num_op_) call trunk_zero_;
if (_this select 2 < 0 && {_res != "0"}) then {
_res = "-" + _res;
};
_res
};
case (2): {
_this set [7, true];
(_this call num_op_) call trunk_zero_
};
};
};
trunk_zero_ = {
private ["_max","_flp"];
_this = toArray _this;
_max = count _this;
_flp = _this find 46;
if (_flp < 0) then {_flp = _max};
for "_i" from _max - 1 to _flp step -1 do {
if (_this select _i == 46) exitWith {_this resize _i};
if (_this select _i != 48) exitWith {_this resize (_i + 1)};
};
for "_i" from 0 to _flp - 2 do {
if (_this select 0 != 48) exitWith {};
_this set [0, 'x'];
_this = _this - ['x'];
};
if (count _this == 0) then [{"0"},{toString _this}]
};
KK_fnc_strNumAdd = {
_this = +_this call num_prep_;
_this call add_prepd_;
};
KK_fnc_strNumSubtract = {
_this = +_this call num_prep_;
_this set [3, (_this select 3) * -1];
_this call add_prepd_;
};
KK_fnc_strNumCompare = {
((+_this call num_prep_) call max_abs_) select 6;
};
KK_fnc_strNumFormat = {
private ["_flp","_max","_num","_k"];
_this = toArray _this - [44];
_flp = _this find 46;
_max = count _this;
call {
if (_flp < 0) exitWith {_this = _this + [46,48,48]};
if (_flp < _max - 3) exitWith {_this resize (_flp + 3)};
if (_flp > _max - 3) exitWith {_this = _this + [48]};
};
_max = count _this;
_num = ""; _k = -4;
for "_i" from _max - 1 to 0 step -1 do {
_k = if (_k == 2) then [{_num = "," + _num; 0},{_k + 1}];
_num = toString [_this select _i] + _num;
};
_num
};
* KK_fnc_strNumAdd / Input: [STRING, STRING] / Output: STRING / Examples:
["1234","5678"] call KK_fnc_strNumAdd; //6912
["1234.567","-1234"] call KK_fnc_strNumAdd; //0.567
["-123.456","-123456.789"] call KK_fnc_strNumAdd; //-123580.245
* KK_fnc_strNumSubtract / Input: [STRING, STRING] / Output: STRING / Examples:
["12345","678"] call KK_fnc_strNumSubtract; //11667
["123.45","678.9"] call KK_fnc_strNumSubtract; //-555.45
["-123.456","12.3"] call KK_fnc_strNumSubtract; //-135.756
* KK_fnc_strNumCompare / Input: [STRING, STRING] / Output: NUMBER (0: number1 == number2, 1: number1 > number2, -1: number1 < number2) / Examples:
["1234.5","567.8"] call KK_fnc_strNumCompare; //1
["-123","-123"] call KK_fnc_strNumCompare; //0
["-123.4","123.4"] call KK_fnc_strNumCompare; //-1
* KK_fnc_strNumFormat / Input: STRING / Output: STRING / Examples:
"123.4567" call KK_fnc_strNumFormat; //123.45 (no rounding)
"12345.6" call KK_fnc_strNumFormat; //12,345.60
"1234567890" call KK_fnc_strNumFormat; //1,234,567,890.00
The add, subtract and compare functions also support formatted numbers if needed:
["-1,234","2,345.67"] call KK_fnc_strNumAdd; //1111.67
["-1,234","2,345.67"] call KK_fnc_strNumSubtract; //-3579.67
["-1,234","2,345.67"] call KK_fnc_strNumCompare; //-1
Enjoy,
KK
EDIT: Fixed a bug (thanks to Chronic)