Конвертация Int64 с разделителем групп разрядов: неужели?!)

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Конвертация Int64 с разделителем групп разрядов: неужели?!)

Сообщение avmaksimov » 04.02.2023 21:32:14

Решил доработать один проект и перевести целое число типа Int64 в строку с разделителем. И вот любимый формат тут не поможет, ибо "%n" жаждет чего-то с точкой. Все остальные решения не далеко ушли (FloatToStrF c ffNumber). Суть проблемы в том, что максимальное значение Int64 - 9 223 372 036 854 775 807, а при любом преобразовании в число с плавающей точкой цифра 7 пропадает и остаётся только: 9 223 372 036 854 775 800 .
Казалось бы подумаешь, потерялась бы семёрка, но мой перфекционизм не даёт покоя. Неужели нельзя никак решить задачу, кроме IntToStr и далее вручную разбивать через разделитель строки? Т.е. существующего решения нет? :mrgreen: :mrgreen: :mrgreen:

UPD. Потеря значащих знаков только на Windows. На Linux не воспроизводится, но не теряет актуальности момент отсутствия готового решения или только костыль с Float.
Последний раз редактировалось avmaksimov 09.02.2023 12:53:23, всего редактировалось 1 раз.
avmaksimov
новенький
 
Сообщения: 21
Зарегистрирован: 17.10.2010 12:38:54

Re: Конвертация Int64 с разделителем групп разрядов: неужели

Сообщение avmaksimov » 05.02.2023 23:46:18

Я постарался сделать свою оптимизированную функцию. В принципе, внутри ей пофигу на тип, но делать такое странное форматирование строки было бы ещё более странным. Тем не менее, для популяризации языка, считаю, что данная функция должна быть must have. Одно дело, когда округляется число с дробной частью, а другое, когда целое число рубится. Нужно было для списка файлов и размеров файлов для "Double Commander".

Надеюсь, кому-то ещё понадобится, даже, если и не добавят в RTL.

Код: Выделить всё
function IntToStrTS(const AValue: SizeUInt): string;
var i, vSrcLen, vSrcI, vSrcNumberNo, vResLen: byte;
begin
  Str(AValue, Result);
  vSrcLen := Result.Length;

  vResLen := vSrcLen + ((vSrcLen - 1) div 3);
  SetLength(Result, vResLen);

  vSrcI := vResLen;
  vSrcNumberNo := 1;

  for i:= vSrcLen downto 1 do
    begin
      Result[vSrcI] := Result[i];
      Dec(vSrcI);
      if(vSrcNumberNo <> vSrcLen) and (vSrcNumberNo mod 3 = 0) then
        begin
          Result[vSrcI] := FormatSettings.ThousandSeparator;
          Dec(vSrcI);
        end;
      Inc(vSrcNumberNo);
    end;
end;


Добавлено спустя 9 часов 19 минут 7 секунд:
Re: Конвертация Int64 с разделителем групп разрядов: неужели?!)
Эх.. Предложил на FPC и один из собратьев предложил похожий модуль: http://svn.code.sf.net/p/flyingsheep/code/trunk/MijnLib/fsiconv.pp. Надеюсь, тоже кому-то будет полезен.

Там использован похожий алгоритм). Наверное, это более оптимальный, чем в RTL при форматировании строки с множеством Insert'ов, что вызовет копирование строки на каждый такой запрос (.
avmaksimov
новенький
 
Сообщения: 21
Зарегистрирован: 17.10.2010 12:38:54

Re: Конвертация Int64 с разделителем групп разрядов: неужели

Сообщение Alex2013 » 07.02.2023 13:18:27

ИМХО:Довольно странная функция.
Зачем там SetLength(Result, vResLen); и т.п. ?
Можно ведь просто написать что-то вроде этого.
Код: Выделить всё
function IntToStrTS(const AValue: SizeUInt): string;
var i, vSrcLen,  , vResLen: byte;
begin
  Str(AValue, Result);
  vSrcLen := Result.Length;
  vResLen := vSrcLen + ((vSrcLen - 1) div 3);
For I:=1 to  vResLen do
if (I < vSrcLen) and ( i mod 3 = 0) then
       Inset(FormatSettings.ThousandSeparator,Result,i);
end;

( Извиняюсь если что-то неверно понял или неучел )
Последний раз редактировалось Alex2013 07.02.2023 14:34:33, всего редактировалось 11 раз(а).
Alex2013
долгожитель
 
Сообщения: 2923
Зарегистрирован: 03.04.2013 11:59:44

Re: Конвертация Int64 с разделителем групп разрядов: неужели

Сообщение avmaksimov » 07.02.2023 13:26:15

Alex2013 писал(а):ИМХО:Довольно странная функция.
Зачем там SetLength(Result, vResLen); и т.п. ?
Можно ведь просто написать что-то вроде этого.
Код: Выделить всё
function IntToStrTS(const AValue: SizeUInt): string;
var i, vSrcLen,  , vResLen: byte;
begin
  Str(AValue, Result);
  vSrcLen := Result.Length;
  vResLen := vSrcLen + ((vSrcLen - 1) div 3);
For I:=1 to  vResLen do
if (I < vSrcLen) and ( i mod 3 = 0) then
       Inset(FormatSettings.ThousandSeparator,Result,i);
end;

( Извиняюсь если что-то неверно понял )

Insert будет в цикле для каждого нуля выделять память для новой строки и затем делаться копия. В моём случае память будет выделена один раз.
avmaksimov
новенький
 
Сообщения: 21
Зарегистрирован: 17.10.2010 12:38:54

Re: Конвертация Int64 с разделителем групп разрядов: неужели

Сообщение iskander » 07.02.2023 14:13:39

avmaksimov писал(а):В моём случае память будет выделена один раз.

Таки два.
iskander
энтузиаст
 
Сообщения: 590
Зарегистрирован: 08.01.2012 18:43:34

Re: Конвертация Int64 с разделителем групп разрядов: неужели

Сообщение avmaksimov » 07.02.2023 14:18:32

iskander писал(а):
avmaksimov писал(а):В моём случае память будет выделена один раз.

Таки два.

Согласен, но я имел в виду, что на первоначальную строку память будет выделена в любом случае. Str переписывать на Паскале нет смысла, т.к. написана на Асме.
avmaksimov
новенький
 
Сообщения: 21
Зарегистрирован: 17.10.2010 12:38:54

Re: Конвертация Int64 с разделителем групп разрядов: неужели

Сообщение RRYTY » 07.02.2023 14:19:31

Десятичные разделители - это вообще странно.
Можно менять только последний символ на правильный. :D
RRYTY
постоялец
 
Сообщения: 187
Зарегистрирован: 25.12.2021 10:00:32

Re: Конвертация Int64 с разделителем групп разрядов: неужели

Сообщение Alex2013 » 07.02.2023 14:35:27

Тогда так
avmaksimov писал(а):Insert будет в цикле для каждого нуля выделять память для новой строки и затем делаться копия. В моём случае память будет выделена один раз.

Точно ! ( К тому уже мой вариант не совсем верно размечет строку )
Alex2013
долгожитель
 
Сообщения: 2923
Зарегистрирован: 03.04.2013 11:59:44

Re: Конвертация Int64 с разделителем групп разрядов: неужели

Сообщение avmaksimov » 07.02.2023 15:26:54

RRYTY писал(а):Десятичные разделители - это вообще странно.
Можно менять только последний символ на правильный. :D

Вы неверно поняли. Речь про разделить тысяч. Читать строку "1 235 656 788" приятнее и понятнее, чем "1235656788".

Ну и не последний символ, а какой-то)) Но речь тут про целые числа. Для чисел с плавающих есть FloatToStrF, но как писал выше при переводе Int64 в Extended теряется целая часть числа на больших числах (.
avmaksimov
новенький
 
Сообщения: 21
Зарегистрирован: 17.10.2010 12:38:54

Re: Конвертация Int64 с разделителем групп разрядов: неужели

Сообщение iskander » 07.02.2023 15:35:02

avmaksimov писал(а):Str переписывать на Паскале нет смысла, т.к. написана на Асме.

А мне попадалась инфа, что Str() довольно-таки неторопливая процедура.
iskander
энтузиаст
 
Сообщения: 590
Зарегистрирован: 08.01.2012 18:43:34

Re: Конвертация Int64 с разделителем групп разрядов: неужели

Сообщение Alex2013 » 07.02.2023 15:42:04

Чисто как вариант "псевдо оптимизации"
( Возможно где-то напутал "с циферками" но просто "как идея" )
Код: Выделить всё
  function IntToStrTS(const AValue: SizeUInt): string;
  var i,J, vSrcLen,  vSCount, vResLen: integer ; //!
  STmp: string;
  begin
    Str(AValue, STmp); // всеравно два раза память выделяется
    vSrcLen :=  STmp.Length;
    vSCount:= ((vSrcLen - 1) div 3) ;
    vResLen := vSrcLen +vSCount;
    SetLength(Result, vResLen);
  J:=3; For I:=1 to vSCount do begin
  If i = vSCount  then J:=vResLen - vSCount*3 ;
  // Чуть криво но работать по идее будет
  if J<>0 then begin
   Move( STmp[vResLen-(i*3)],Result[vResLen-(i*4)+1],J); // ???
     Result[vResLen-(i*4)]:=' ';// или  FormatSettings.ThousandSeparator
    end
  end
  end;


Добавлено спустя 33 минуты 16 секунд:
Re: Конвертация Int64 с разделителем групп разрядов: неужели?!)
iskander писал(а):А мне попадалась инфа, что Str() довольно-таки неторопливая процедура.

Не то что-бы "неторопливая" скорее просто достаточно сложно устроенная.
Последний раз редактировалось Alex2013 07.02.2023 20:39:02, всего редактировалось 4 раз(а).
Alex2013
долгожитель
 
Сообщения: 2923
Зарегистрирован: 03.04.2013 11:59:44

Re: Конвертация Int64 с разделителем групп разрядов: неужели

Сообщение iskander » 07.02.2023 16:16:05

Alex2013 писал(а):Чуть криво но работать по идее будет

А мне что-то кажется, что оно даже не скомпилируется. :)
iskander
энтузиаст
 
Сообщения: 590
Зарегистрирован: 08.01.2012 18:43:34

Re: Конвертация Int64 с разделителем групп разрядов: неужели

Сообщение Alex2013 » 07.02.2023 16:28:16

iskander писал(а):
Alex2013 писал(а):Чуть криво но работать по идее будет

А мне что-то кажется, что оно даже не скомпилируется. :)

Проверял компилирует .
Alex2013
долгожитель
 
Сообщения: 2923
Зарегистрирован: 03.04.2013 11:59:44

Re: Конвертация Int64 с разделителем групп разрядов: неужели

Сообщение iskander » 07.02.2023 17:00:40

Alex2013 писал(а):Чисто как вариант "псевдо оптимизации"
( Возможно где-то напутал "с циферками" но просто "как идея" )
Код: Выделить всё
  function IntToStrTS(const AValue: SizeUInt): string;
  var i,J, vSrcLen,  vSCount, vResLen: integer ; //!
  STmp: string;
  begin
    Str(AValue, STmp); // всеравно два раза память выделяется
    vSrcLen := Result.Length;
    vSCount:= ((vSrcLen - 1) div 3) ;
    vResLen := vSrcLen +vSCount;
    SetLength(Result, vResLen);
  J:=3; For I:=1 to vSCount do begin
  If i = vSCount  then J:=vResLen - vSCount*3 ;
  // Чуть криво но работать по идее будет
  if J<>0 then begin
   Move( STmp[vResLen-(i*3)],Result[vResLen-(i*4)+1],J); // ???
     Result[vResLen-(i*4)]:=' ';// или  FormatSettings.ThousandSeparator
    end
  end
  end;

...


Ага, кажется, исправил. Ещё было бы неплохо, если бы она какой-нибудь результат возвращала.
iskander
энтузиаст
 
Сообщения: 590
Зарегистрирован: 08.01.2012 18:43:34

Re: Конвертация Int64 с разделителем групп разрядов: неужели

Сообщение RRYTY » 07.02.2023 17:10:08

avmaksimov писал(а):...
Ну и не последний символ, а какой-то)) Но речь тут про целые числа. Для чисел с плавающих есть FloatToStrF, но как писал выше при переводе Int64 в Extended теряется целая часть числа на больших числах (.


Вот это уже косяк...
RRYTY
постоялец
 
Сообщения: 187
Зарегистрирован: 25.12.2021 10:00:32

След.

Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 16

Рейтинг@Mail.ru