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

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

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

avmaksimov
новенький
Сообщения: 21
Зарегистрирован: 17.10.2010 12:38:54
Контактная информация:

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

Сообщение avmaksimov »

Решил доработать один проект и перевести целое число типа 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 11:53:23, всего редактировалось 1 раз.
avmaksimov
новенький
Сообщения: 21
Зарегистрирован: 17.10.2010 12:38:54
Контактная информация:

Сообщение avmaksimov »

Я постарался сделать свою оптимизированную функцию. В принципе, внутри ей пофигу на тип, но делать такое странное форматирование строки было бы ещё более странным. Тем не менее, для популяризации языка, считаю, что данная функция должна быть 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/co ... fsiconv.pp. Надеюсь, тоже кому-то будет полезен.

Там использован похожий алгоритм). Наверное, это более оптимальный, чем в RTL при форматировании строки с множеством Insert'ов, что вызовет копирование строки на каждый такой запрос (.
Alex2013
долгожитель
Сообщения: 3220
Зарегистрирован: 03.04.2013 11:59:44

Сообщение 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;
( Извиняюсь если что-то неверно понял или неучел )
Последний раз редактировалось Alex2013 07.02.2023 13:34:33, всего редактировалось 11 раз.
avmaksimov
новенький
Сообщения: 21
Зарегистрирован: 17.10.2010 12:38:54
Контактная информация:

Сообщение avmaksimov »

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 будет в цикле для каждого нуля выделять память для новой строки и затем делаться копия. В моём случае память будет выделена один раз.
iskander
энтузиаст
Сообщения: 630
Зарегистрирован: 08.01.2012 18:43:34

Сообщение iskander »

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

Сообщение avmaksimov »

iskander писал(а):
avmaksimov писал(а):В моём случае память будет выделена один раз.
Таки два.
Согласен, но я имел в виду, что на первоначальную строку память будет выделена в любом случае. Str переписывать на Паскале нет смысла, т.к. написана на Асме.
RRYTY
постоялец
Сообщения: 266
Зарегистрирован: 25.12.2021 09:00:32

Сообщение RRYTY »

Десятичные разделители - это вообще странно.
Можно менять только последний символ на правильный. :D
Alex2013
долгожитель
Сообщения: 3220
Зарегистрирован: 03.04.2013 11:59:44

Сообщение Alex2013 »

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

Сообщение avmaksimov »

RRYTY писал(а):Десятичные разделители - это вообще странно.
Можно менять только последний символ на правильный. :D
Вы неверно поняли. Речь про разделить тысяч. Читать строку "1 235 656 788" приятнее и понятнее, чем "1235656788".

Ну и не последний символ, а какой-то)) Но речь тут про целые числа. Для чисел с плавающих есть FloatToStrF, но как писал выше при переводе Int64 в Extended теряется целая часть числа на больших числах (.
iskander
энтузиаст
Сообщения: 630
Зарегистрирован: 08.01.2012 18:43:34

Сообщение iskander »

avmaksimov писал(а): Str переписывать на Паскале нет смысла, т.к. написана на Асме.
А мне попадалась инфа, что Str() довольно-таки неторопливая процедура.
Alex2013
долгожитель
Сообщения: 3220
Зарегистрирован: 03.04.2013 11:59:44

Сообщение Alex2013 »

Чисто как вариант "псевдо оптимизации"
( Возможно где-то напутал "с циферками" но просто "как идея" )

Код: Выделить всё

  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 19:39:02, всего редактировалось 4 раза.
iskander
энтузиаст
Сообщения: 630
Зарегистрирован: 08.01.2012 18:43:34

Сообщение iskander »

Alex2013 писал(а):Чуть криво но работать по идее будет
А мне что-то кажется, что оно даже не скомпилируется. :)
Alex2013
долгожитель
Сообщения: 3220
Зарегистрирован: 03.04.2013 11:59:44

Сообщение Alex2013 »

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

Сообщение iskander »

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;
...
Ага, кажется, исправил. Ещё было бы неплохо, если бы она какой-нибудь результат возвращала.
RRYTY
постоялец
Сообщения: 266
Зарегистрирован: 25.12.2021 09:00:32

Сообщение RRYTY »

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