PDA

Просмотр полной версии : [Вопрос] Превратить "1000000" в "1 000 000"



Josan_Solomon
08.09.2018, 05:11
Hello, World! Как максимально эффективно вытащить из числа (желательно именно числа, так как числа будут большие, порядком до 10 миллиардов, что превышает ограничение valstr() ) строку с разделением каждого третьего знака для облегчения визуализации? Мне нужен метод с 1 параметром числового типа (если можно так выразиться, учитывая что pawn бестиповый), который будет возвращать строковое представление числа с разделителями в виде пробела, или запятой. Пробовал математически, циклически и даже с побайтовым переносом символов из ячейки в ячейку с постановкой запятых каждые три ячейки с конца, но всё не то. Уповаю на вашу помощь. Вот пример того, как хотелось бы видеть команду:

print(intcommas(123456789000)); // в консоли: "123 456 789 000"


Важна работоспособность с большими числами

ziggi
08.09.2018, 10:55
http://forum.sa-mp.com/showthread.php?t=184328
Там все ссылки не работают, возьми отсюда: https://github.com/Open-GTO/Open-GTO/blob/master/sources/lib/formatnumber.inc

DeimoS
08.09.2018, 11:21
Есть ещё такой вариант
stock AddCommasToInt(number, delimiter[2] = ".")
{
new int_string[10+3+1+1];

format(int_string, sizeof(int_string), "%d", number >= 0 ? number : -number);//Если число положительное - запишем его в обычном виде. Иначе - добавим ещё один минус, дабы сделать его положительным


new value = strlen(int_string);// Запишем в value размер строки с числом

switch(value)
{
case 4..6: // Если переданное число содержит от 4-х до 6-и символов, добавим один разделитель
strins(int_string, delimiter, value-3,1);
case 7..9: // Если от 7-и до 9-и - два разделителя
strins(int_string, delimiter, value-3,1),
strins(int_string, delimiter, value-6,1);
case 10..12: // Если от 10-и до 12-и - три разделителя
strins(int_string, delimiter, value-3,1),
strins(int_string, delimiter, value-6,1),
strins(int_string, delimiter, value-9,1);
}

if(number < 0) strins(int_string, "-", 0); // Если число отрицательное, вернём ему минус
return int_string;
}
Только нужно будет добавить дополнительные условия, в зависимости от того, каким может быть максимальное число

ziggi
08.09.2018, 11:55
Есть ещё такой вариант
stock AddCommasToInt(number, delimiter[2] = ".")
{
new int_string[10+3+1+1];

format(int_string, sizeof(int_string), "%d", number >= 0 ? number : -number);//Если число положительное - запишем его в обычном виде. Иначе - добавим ещё один минус, дабы сделать его положительным


new value = strlen(int_string);// Запишем в value размер строки с числом

switch(value)
{
case 4..6: // Если переданное число содержит от 4-х до 6-и символов, добавим один разделитель
strins(int_string, delimiter, value-3,1);
case 7..9: // Если от 7-и до 9-и - два разделителя
strins(int_string, delimiter, value-3,1),
strins(int_string, delimiter, value-6,1);
case 10..12: // Если от 10-и до 12-и - три разделителя
strins(int_string, delimiter, value-3,1),
strins(int_string, delimiter, value-6,1),
strins(int_string, delimiter, value-9,1);
}

if(number < 0) strins(int_string, "-", 0); // Если число отрицательное, вернём ему минус
return int_string;
}
Только нужно будет добавить дополнительные условия, в зависимости от того, каким может быть максимальное число

Вот здесь есть ещё: http://wiki.sa-mp.com/wiki/AddThousandsSeparators#Definition

Josan_Solomon
08.09.2018, 19:37
Увы, format и valstr, как я уже сказал, не способны справиться с большими числами. Пробовал тэг long (хотя еще ни разу не встречал его в pawn), но снова не то. Как можно обойти это, чтобы работать с большими числами?

Batya_Montes
08.09.2018, 21:30
Увы, format и valstr, как я уже сказал, не способны справиться с большими числами. Пробовал тэг long (хотя еще ни разу не встречал его в pawn), но снова не то. Как можно обойти это, чтобы работать с большими числами?

а ничего что твое число "123456789000" выходит за рамки возможностей 32-битного языка и для него уже нужен тип bigint, коего нет в павн?

DeimoS
08.09.2018, 23:11
Как уже выше заметили, это невозможно из-за ограничений языка.
Тут два выхода есть:
1) Хранить число как строку
2) Разбить число на 2 переменные
Оба варианта потребуют написания своего алгоритма для обработки чисел.

Josan_Solomon
09.09.2018, 03:16
Неожиданное фиаско.. А что по вашему более экономно?

- - - Добавлено - - -


Там все ссылки не работают, возьми отсюда: [url]https://github.com/Open-GTO/Open-GTO/blob/master/sources/lib/formatnumber.inc
Скорее всего, это ошибка, но все же, надежда умирает последней. В примере исходника той ссылки есть такой код:

thousand seperator - char between thousands
new mynumber = 5000000000;
FormatNumber( myfloat ); // gives 5 000 000 000
FormatNumber( myfloat, 0, ',' ); // gives 5,000,000,000
FormatNumber( myfloat, 0, 0 ); // gives 5000000000

Число в нем - 5 миллиардов, что превышает 32 битное 2,147,483,648. Это рабочий пример, или Slice просто не доглядел?

VVWVV
09.09.2018, 04:14
Неожиданное фиаско.. А что по вашему более экономно?

- - - Добавлено - - -


Скорее всего, это ошибка, но все же, надежда умирает последней. В примере исходника той ссылки есть такой код:

thousand seperator - char between thousands
new mynumber = 5000000000;
FormatNumber( myfloat ); // gives 5 000 000 000
FormatNumber( myfloat, 0, ',' ); // gives 5,000,000,000
FormatNumber( myfloat, 0, 0 ); // gives 5000000000

Число в нем - 5 миллиардов, что превышает 32 битное 2,147,483,648. Это рабочий пример, или Slice просто не доглядел?

Я бы не сказал, что 2,147,483,648 - предел 32 битного числа, скорее 31 битного (ибо самый старший бит - показатель знака). Для 32 битного это число в два раза больше. К тому же, названия переменной и переданного аргумента не совпадают. Название переменной, переданной в качестве аргумента, содержит слово float. Не трудно догадаться, что 32 битный float может содержать довольно большое значение из-за стандарта хранения чисел с плавающей точкой.

UPD: Да, документация в файле неточная, поэтому советую смотреть пример в теме, там вроде бы хороший пример.

Josan_Solomon
09.09.2018, 06:21
Промелькнула шальная мысль хранить значение во float(а потом убирать точку), потом дошло что сам язык 32-х битный и ничего с этим ограничением уже не поделаешь. Как печально.. А как вы хранили бы большие числа?

- - - Добавлено - - -

И какое максимальное значение у float? Не нашел список диапазона значений для pawn

- - - Добавлено - - -

Поэкспериментировал и не нашел четкого ограничения, но увы, точность стала пропадать

VVWVV
09.09.2018, 09:08
Промелькнула шальная мысль хранить значение во float(а потом убирать точку), потом дошло что сам язык 32-х битный и ничего с этим ограничением уже не поделаешь. Как печально.. А как вы хранили бы большие числа?

- - - Добавлено - - -

И какое максимальное значение у float? Не нашел список диапазона значений для pawn

- - - Добавлено - - -

Поэкспериментировал и не нашел четкого ограничения, но увы, точность стала пропадать

Зачем вам такие числа? Если вы хотите хранить числа в диапазоне [0, 4294967296), то можно использовать библиотеку ulong (https://github.com/tdworg/samp-include-ulong). Тем не менее я не вижу абсолютно никакого смысла использовать в SA:MP значения больше имеющихся.

Daniel_Cortez
23.09.2018, 20:27
Есть ещё такой вариант
stock AddCommasToInt(number, delimiter[2] = ".")
{
new int_string[10+3+1+1];

format(int_string, sizeof(int_string), "%d", number >= 0 ? number : -number);//Если число положительное - запишем его в обычном виде. Иначе - добавим ещё один минус, дабы сделать его положительным


new value = strlen(int_string);// Запишем в value размер строки с числом

switch(value)
{
case 4..6: // Если переданное число содержит от 4-х до 6-и символов, добавим один разделитель
strins(int_string, delimiter, value-3,1);
case 7..9: // Если от 7-и до 9-и - два разделителя
strins(int_string, delimiter, value-3,1),
strins(int_string, delimiter, value-6,1);
case 10..12: // Если от 10-и до 12-и - три разделителя
strins(int_string, delimiter, value-3,1),
strins(int_string, delimiter, value-6,1),
strins(int_string, delimiter, value-9,1);
}

if(number < 0) strins(int_string, "-", 0); // Если число отрицательное, вернём ему минус
return int_string;
}
Немного поздно, но всё же: функция неправильно выводит cellmin, вместо "-2147483648" выводится "---" (причина кроется в баге в функции format()). Решение из formatnumber.inc тоже фейлит на cellmin.

Поделюсь своим костылём:

stock NumToString(dest[], num, sep = '\'', size = sizeof(dest))
{ // by Daniel_Cortez \\ pro-pawn.ru
if (num == cellmin)
{
format(dest, size, "-2%c147%c483%c648", sep, sep, sep);
return 14;
}
static neg, len, offs;
new numstr[11];
if ((neg = (num < 0)))
{
dest[0] = '-';
size--;
num = -num;
}
format(numstr, sizeof(numstr), "%d", num);
offs = ((len = strlen(numstr)) - 1) % 3 + 1;
switch (len)
{
case 1..3:
return strunpack(dest[neg], numstr, size) + neg;
case 4..6:
return format(dest[neg], size, "%.*s%c%.3s", offs, numstr, sep, numstr[offs]), (len + neg + 1);
case 7..9:
return format(dest[neg], size, "%.*s%c%.3s%c%.3s", offs, numstr, sep, numstr[offs], sep, numstr[3+offs]), (len + neg + 2);
}
format(dest[neg], size, "%.*s%c%.3s%c%.3s%c%.3s", offs, numstr, sep, numstr[offs], sep, numstr[3+offs], sep, numstr[6+offs]);
return len + neg + 3;
}

Из плюсов данного варианта: отсутствие возврата массива через стек (вместо этого пользователь сам должен указать массив для записи), правильный вывод cellmin, функция возвращает длину строки с числом.

VVWVV
24.09.2018, 17:04
...

Еще можно так это сделать.

stock
ulong_string(dest[], ulong:value, bool:is_packed = false,
delimiter, size = sizeof dest)
{
dest[0] = '0';
if (value) {
if (is_packed)
size <<= 2;
new
end = ulong_getDigitCount(value),
mod = 0,
part_size = 0,
out_chr = 0;
while (value) {
// dest[end] = '0' + (value % 10)
#emit load.s.pri value
#emit const.alt 10
#emit udiv
#emit stor.s.pri value
#emit stor.s.alt mod

if (++part_size == 4) {
part_size = 0;
out_chr = delimiter;
} else if (--end < size) {
out_chr = '0' + mod;
}
if (is_packed) {
dest{end} = out_chr;
} else {
dest[end] = out_chr;
}
}
}
return 0;
}