powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Программирование [игнор отключен] [закрыт для гостей] / IntToPChar на ASM для DELPHI
54 сообщений из 54, показаны все 3 страниц
IntToPChar на ASM для DELPHI
    #39379770
Midgard90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Всем доброго времени суток!
Существуют ли программисты, помнящие ASM? ))
Столкнулись с проблемой: в Delphi нет подобной функции, есть IntToStr, но на выходе string. В большом проекте нужен именно PChar. На С++ это itoa, atoi.
Сделал костыль из стандартной IntToStr путём комментирования функции задела строки и дёргаю в наглую после выхода из неё по указателю шмат памяти:

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
function IntToText(Buffer: PChar; MaxLen: Cardinal; Value: Integer): Integer;

  procedure __Int32ToStr(Value: Integer);
  asm
    PUSH    ESI
    MOV     ESI, ESP
    SUB     ESP, 16
    XOR     ECX, ECX       // base: 0 for signed decimal
    PUSH    EDX            // result ptr
    XOR     EDX, EDX       // zero filled field width: 0 for no leading zeros
    CALL    __CvtInt
    MOV     EDX, ESI
    POP     EAX            // result ptr
    ADD     ESP, 16
    POP     ESI
  end;
var
  Pter : PChar;
  Length : integer;
begin
  __Int32ToStr(Value);

  asm
    mov Pter, EAX
    mov Length , ECX
  end;
  if Length <= MaxLen then begin
    Move(Pter^,Buffer^,Length);
    Result := Length;
  end
  else begin
    Result := -1;
    exit;
  end;
end;



Тоже самое для int64 и overlode. Если кому пригодиться, могу скинуть для int64. Функцию __CvtInt тоже пришлось перетянуть из сорцов делфи). Мне не понравилось это решение.

Нашёл 2 исходника на асм по такой же задаче для int и int64, единственное - использовались регистры ax,bx,cx... и т д. и генерился 16 битный код. Могу скинуть также оба исходника и ссыль на них.
Так вот суть вопроса: найденая вставочка int в дельфе отрабатывает на ура, быстрее чем костыльный вариант. Но с int64 возникли трудности. я не мастер asmа, но чую не так я записываю в стек или перемещаю указатель.
Вот исходник со статьи:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
use16                       ;Генерировать 16-битный код
org 100h                    ;Программа начинается с адреса 100h
    jmp start               ;Перепрыгнуть данные
;-------------------------------------------------------------------------------
; Данные
a       dq 1600000000000000
b       dq 33000000
c       dq 12345678901234567890
s_a     db 'a = $'
s_b     db 'b = $'
s_c     db 'c = $'
endline db 13,10,'$'
s_pak   db 'Press any key...$'
;-------------------------------------------------------------------------------
; Код
start:
    mov di,s_a
    call print_str          ;Вывод строки 'a = '
    mov bx,a
    call print_qword_udec   ;Вывод числа a
    call print_endline      ;CR+LF
 
    mov di,s_b
    call print_str          ;Вывод строки 'b = '
    mov bx,b
    call print_qword_udec   ;Вывод числа b
    call print_endline      ;CR+LF
 
    mov di,s_c
    call print_str          ;Вывод строки 'c = '
    mov bx,c
    call print_qword_udec   ;Вывод числа c
    call print_endline      ;CR+LF
 
    mov di,s_pak
    call print_str          ;Вывод строки 'Press any key...'
    mov ah,8                ;\
    int 21h                 ;/ Ввод символа без эха
    mov ax,4C00h            ;\
    int 21h                 ;/ Завершение программы
 
;-------------------------------------------------------------------------------
;Процедура вывода учетверённого слова на консоль в десятичном виде (без знака)
; BX - адрес qword
print_qword_udec:
    push di
    sub sp,22               ;Выделение места в стеке для буфера
    mov di,sp               ;DI = адрес буфера
    push word[bx+6]         ;\
    push word[bx+4]         ; \
    push word[bx+2]         ; / Помещение qword в стек
    push word[bx]           ;/
    call qword_to_udec_str  ;Преобразование qword в строку
    mov byte[di],'$'        ;Добавление символа конца строки
    mov di,sp               ;DI = адрес буфера со строкой
    call print_str          ;Вывод строки на консоль
    add sp,22               ;Восстановление указателя стека
    pop di
    ret
 
;-------------------------------------------------------------------------------
;Процедура преобразования учетверённого слова
;в строку в десятичном виде (без знака)
;Вход: qword в стеке (младший байт по младшему адресу!)
;  DI - буфер для строки (20 символов). Значение регистра не сохраняется.
qword_to_udec_str:
    push bp                 ;Сохранение BP
    mov bp,sp               ;BP=SP
    push ax                 ;Сохранение используемых регистров
    push cx
    push bx
    push si
 
    xor cx,cx               ;Обнуление CX
    mov bx,10               ;В BX делитель (10 для десятичной системы)
 
.lp1:                       ;Цикл получения остатков от деления
    xor dx,dx
    mov si,8                ;SI = смещение слова для деления
 
.div64:                     ;Цикл 64-битного деления
    mov ax,[bp+si+2]        ;Загрузка слова в AX
    div bx                  ;AX = (DX:AX)/10; DX = остаток
    mov [bp+si+2],ax        ;Сохранение результата деления слова
    sub si,2                ;Уменьшение смещения на 2
    jnz .div64              ;Переход к началу цикла, если SI != 0
 
    add dl,'0'              ;Преобразование остатка в код символа
    push dx                 ;Сохранение в стеке
    inc cx                  ;Увеличение счетчика символов
    or ax,[bp+6]            ;\
    or ax,[bp+8]            ; > Проверка частного на равенство 0 ;)
    or ax,[bp+10]           ;/
    jnz .lp1                ;Переход к началу цикла, если частное не 0.
 
.lp2:                       ;Цикл извлечения символов из стека
    pop dx                  ;Восстановление символа из стека
    mov [di],dl             ;Сохранение символа в буфере
    inc di                  ;Инкремент адреса буфера
    loop .lp2               ;Команда цикла
 
    pop si                  ;Восстановление сохранённых регистров
    pop bx
    pop cx
    pop ax
    leave                   ;Восстановление SP и BP
    ret 8                   ;Возврат с выталкиванием 8 байт из стека



Моя переделка заключалась в замене всех основных регистров на 32х битные, то есть ax на eax, bx на ebx и т д. так как 16 битный дельфа строить отказывается. Если надо скину переделку. Дельфа зацикливается при делении числа на 10 и записи остатка. Пока стек не кончится)) где я косячу? Данный код работает в asm - проверял.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39379777
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
компилятор дельфи такой страшный код генерит, что на асме писать нужно?
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39379784
Midgard90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Нет) Можно и обойтись средствами Делфи. Важна скорость, это ключевой момент. большие объёмы данных, много расчётов. Поэтому прибегли к АСМ. Для примера нагрузочных тестов 100 000 000 преобразований на каждом ядре:
Функциями дельфи - профит -0,38, процент скорости - 30%
Костыльный вариант - профит +0,75, процент скорости - 81%
Самодел из взятых исходников - профит -0,95, процент скорости - 90%

Это если чисто int.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39379795
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Midgard90Но с int64 возникли трудности. я не мастер asmа, но чую не так я записываю в стек или перемещаю указатель.

Дельфи x64 использует Microsoft's Win64 calling convention
соответственно первые 4 параметра - RCX,RDX,R8,R9
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39379796
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39379821
Midgard90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Спасибо за инфу, но немного не то. Речь идёт о Delphi x32 (Delphi 7). Судя по их исходникам с asm в те года 64е регистры были мечтой)
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39379826
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Midgard90,

так где лыжи не едут - в x86 ?

что надо то - int64 в pchar?
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39379837
Midgard90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Да, всё верно.
Лыжи не ежут в старой Delphi 7, в асмовой вставке int64ToPChar. IntToPChar сработала на ура, сложностей не было - число ложится в регистр как есть.
int64ToPChar - не как есть, как я понял это указатель в стеке, или не указатель, инфы не нарыл. видать из за этого я не правильно кладу старшую и младшую части, и из за этого соответственно кривит сам цикл деления на 10
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39379854
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Midgard90int64ToPChar - не как есть, как я понял это указатель в стеке, или не указатель, инфы не нарыл. видать из за этого я не правильно кладу старшую и младшую части, и из за этого соответственно кривит сам цикл деления на 10

какой бы мусор на регистры не попал - зацикливаться ничего не должно,
на выходе максимум 19 цифр.

а команда div у интела изрядно косая.
ничего хорошего из лобового деления edx:eax на 10 не получится
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39379860
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
сколь неприятна процедура деления int64 на x86 - можно увидеть - http://www.jbox.dk/sanos/source/lib/lldiv.asm.html
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39379894
Midgard90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Мысль понял, спасибо за совет!)
Буду курить данный пример. Ещё раз благодарю за отзывчивость!

Но вот кстати!) В исходных сорцах Делфи 7 похожий алгоритм на тот, что я нарыл. За исключением что я в стек кидаю остатки, а там как я понял в память сразу пишут. Но тоже div))
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39379898
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Midgard90,

1. Вместо деления но 10 можно умножать на обратный.
2. Вместо деления на 10 можно делить на 100 и брать из таблицы сразу 2 цифры.
3. Можно длинное число разбить на 2 куска и разбираться с каждым отдельно.
4. Можно срисовать код с современной Delphi и не мучиться.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39379941
Пётр Седов
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Midgard90, может сначала попробовать Pascal-код?
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
function int32_to_text(buf: pchar; buf_len: integer; value: integer): integer;
const
  buf2_len = 11; // самое длинное, что может быть -- это '-2147483648' = -pow(2, 31)
var
  n: cardinal;
  buf2: array [0 .. buf2_len - 1] of char;
  p, result_len: integer;
begin
  // считаем abs(value)
  if value >= 0 then begin
    n := value;
  end else begin
    n := cardinal(-(value + 1)) + 1; // единицы, чтобы не было переполнения, когда value = -2147483648
  end;

  p := buf2_len;
  while n >= 10 do begin
    dec(p);
    buf2[p] := chr(ord('0') + n mod 10);
    n := n div 10;
  end;
  dec(p);
  buf2[p] := chr(ord('0') + n);

  if value < 0 then begin
    dec(p);
    buf2[p] := '-';
  end;

  result_len := buf2_len - p;
  if result_len <= buf_len then begin
    move(buf2[p], buf^, result_len);
    result := result_len;
  end else begin
    result := -1;
  end;
end;

function int64_to_text(buf: pchar; buf_len: integer; value: int64): integer;
const
  buf2_len = 20; // самое длинное, что может быть -- это '-9223372036854775808' = -pow(2, 63)
var
  n: int64; // в Delphi 7 нет uint64
  buf2: array [0 .. buf2_len - 1] of char;
  p, result_len: integer;
  part: cardinal;
begin
  // считаем abs(value)
  if value >= 0 then begin
    n := value;
  end else begin
    // hardcode для минимального значения, потому что не можем сделать '-value'
    if value = -9223372036854775807 - 1 then begin
      result_len := 20;
      if result_len <= buf_len then begin
        move('-9223372036854775808', buf^, result_len);
        result := result_len;
      end else begin
        result := -1;
      end;
      exit;
    end;
    n := -value;
  end;

  p := buf2_len;

  // разбиваем 64-битное число на части, чтобы почти всю работу сделать 32-битными операциями

  // берём первую часть
  part := n mod 1000000000;
  n := n div 1000000000;

  // пишем первую часть
  while part >= 10 do begin
    dec(p);
    buf2[p] := chr(ord('0') + part mod 10);
    part := part div 10;
  end;
  dec(p);
  buf2[p] := chr(ord('0') + part);

  if n <> 0 then begin // если что-то осталось
    // дописываем первую часть, чтобы было ровно 9 цифр
    while p > buf2_len - 9 do begin
      dec(p);
      buf2[p] := '0';
    end;

    // берём вторую часть
    part := n mod 1000000000;
    n := n div 1000000000;

    // пишем вторую часть
    while part >= 10 do begin
      dec(p);
      buf2[p] := chr(ord('0') + part mod 10);
      part := part div 10;
    end;
    dec(p);
    buf2[p] := chr(ord('0') + part);

    if n <> 0 then begin // если что-то осталось
      // дописываем вторую часть, чтобы было ровно 9 цифр
      while p > buf2_len - 18 do begin
        dec(p);
        buf2[p] := '0';
      end;

      // пишем третью часть (всего одна цифра)
      assert(n <= 9);
      dec(p);
      buf2[p] := chr(ord('0') + cardinal(n));
    end;
  end;

  if value < 0 then begin
    dec(p);
    buf2[p] := '-';
  end;

  result_len := buf2_len - p;
  if result_len <= buf_len then begin
    move(buf2[p], buf^, result_len);
    result := result_len;
  end else begin
    result := -1;
  end;
end;

Если скорость не устроит, тогда уже переписывать на ассемблере.

Изопропилкомпилятор дельфи такой страшный код генерит, что на асме писать нужно?Если Delphi-йский компилятор для кода типа «n mod 10 ... n div 10» генерирует 2 инструкции div, то да :).

Aleksandr Sharahov3. Можно длинное число разбить на 2 куска и разбираться с каждым отдельно.Мне удалось разбить только на 3 части.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39379947
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пётр СедовЕсли Delphi-йский компилятор для кода типа «n mod 10 ... n div 10» генерирует 2 инструкции div, то да :).
если немного подумать, а не переводить стрелки на компилятор, то легко понять, что есть алгоритм с одним делением:
d:=n div 10;
r:=n - d * 10;



Пётр СедовМне удалось разбить только на 3 части.
если проявить смекалку, например, перейти к беззнаковым числам и поработать со старшими битами и с обратными числами, то удается получить 2 части.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39379995
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пётр Седов
Код: pascal
1.
2.
// если что-то осталось
      // дописываем вторую часть, чтобы было ровно 9 цифр


этого можно избежать изначально заполнив весь буфер нулями
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380024
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahovесли проявить смекалку
многое можно сделать - без делений в цикле обойтись, например.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380035
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ИзопропилAleksandr Sharahovесли проявить смекалку
многое можно сделать - без делений в цикле обойтись, например.
Можно пример как обойтись вообще без делений в цикле?
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380036
Midgard90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Пётр Седов,

Благодарю! Примерно тоже самое и было сперва на дельфях писано, медленно) Но идея разбить на 2 куска и вызвать процедурку для простого уже intа - мне кажется будет выигрышной. Главное не запутаться с переходящим, дёрнуть заранее знак и склеить в конце 2 массива и их длины. сегодня попробую, вечером кину код)

Всех благодарю за советы!
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380060
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima TИзопропилпропущено...

многое можно сделать - без делений в цикле обойтись, например.
Можно пример как обойтись вообще без делений в цикле?
поделить можно один раз, а затем умножать
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
void itoa_x(char *buf, uint32_t val)
{
	double tmp = val / 1e9;
	for (size_t i = 0; i < 10; i++)
	{
		int digit = (int)tmp;
		buf[i] = '0' + (char)digit;
		tmp = (tmp - digit) * 10.0;
	}
}



дальше - переход к фиксированной точке, разделение на группы и т д.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380064
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Не работает, погрешности преобразования double мешают
Код: sql
1.
itoa_x(11) => '0000000010'


Тест
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
void test() {
	char buf[11] = {0};
	for(int i = 1; i < 1000000000; i++) {
		itoa_x(buf, i);
		if(atoi(buf) != i) {
			printf("itoa_x(%d) => '%s'\n", i, buf);
			break;
		}
	}
}

...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380067
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если погрешность добавить, то вроде работает
Код: plaintext
1.
		tmp = (tmp - digit + 1e-10) * 10.0;
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380076
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Преобразования double-int тоже не легкая операция.
Затестил время, деление целых почти вдвое быстрее.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
void itoa_d(char *buf, uint32_t val) {
	for (size_t i = 10; i--; )	{
		int v = val / 10;
		buf[i] = '0' + (char)(val - v * 10);
		val = v;
	}
}
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380080
Midgard90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ребята не гоните коней)))
вещественные не нужны) и есть функция в дельфях вроде FloatToPChar если не ошибаюсь))
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380082
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T,

я же сразу сказал, что это только направление, реально нужно на фиксированную точку перевести
придётся сделать ))
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380086
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima TМожно пример как обойтись вообще без делений в цикле?

1. Заменить деление умножением, умножать на обратный.
2. Заменить деление вычитанием, делить в столбик.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380088
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov1. Заменить деление умножением, умножать на обратный.
Путаешь. Вычитание можно заменить на сложение с обратным, но к делению это неприменимо.
Aleksandr Sharahov2. Заменить деление вычитанием, делить в столбик.
Быстрее не станет, слишком много операций.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380097
Midgard90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
На похмельную голову вышло как-то так:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
function Int64ToText(Arg: PAnsiChar; Value: Int64):integer; assembler;
var
  Ch2 : array [0..10] of Char;
  PCh1,Pch2 :PAnsiChar;
  Val : int64;
  part, n, count1,count2: integer;
begin
  Ch2 := ('          ');
  val := value;
  PCh2 := @Ch2;
  part := val mod 1000000000;
  n := val div 1000000000;
  Count1 := intToText(Arg,n);
  Count2 := intToText(PCh2,abs(part));
  Result := Count1 + Count2;
  StrCopy(StrEnd(Arg), PCh2);
end;



Сама IntToText(та самая самоделка):
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
function IntToText(Arg: PAnsiChar; Value: Integer):integer; assembler;
asm
//Подготовка:
    push eax
    push ebx
    push ecx
    push edx
    push esi
    mov esi,eax
    mov eax,edx
    mov ebx,10               //;В BX делитель (10 для десятичной системы)
    xor ecx,ecx               //;Обнуление CX
    mov byte [esi],0
//Проверяем знак:
    test eax,eax              //;Проверка знака AX
    jns @wtsds_no_sign       //;Если >= 0, преобразуем как беззнаковое
    mov byte[esi],'-'        //;Добавление знака в начало строки
    inc esi                  //;Инкремент DI
    neg eax                  //;Изменение знака значения AX
@wtsds_no_sign:
@wtuds_lp1:                  //;Цикл получения остатков от деления
    xor edx,edx               //;Обнуление старшей части двойного слова
    div ebx                  //;Деление AX=(DX:AX)/BX, остаток в DX
    add dl,'0'              //;Преобразование остатка в код символа
    push edx                 //;Сохранение в стеке
    inc ecx                  //;Увеличение счетчика символов
    test eax,eax              //;Проверка AX
    jnz @wtuds_lp1           //;Переход к началу цикла, если частное не 0.
    mov Result,ecx
@wtuds_lp2:                  //;Цикл извлечения символов из стека
    pop edx                  //;Восстановление символа из стека
    mov [esi],edx             //;Сохранение символа в буфере
    inc esi                  //;Инкремент адреса буфера
    loop @wtuds_lp2          //;Команда цикла
    pop esi
    pop edx
    pop ecx
    pop ebx
    pop eax
end;



Погонял на числах, вроде работает. По скорости отпишу после 9го числа, все тесты на работе)
Всем спасибо за советы! Код ещё в порядок надо будет привести и сделать owerlode, но это мелочи.
Ещё раз благодарен всем за помощь! и с прошедшими и наступающими)
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380098
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima TAleksandr Sharahov1. Заменить деление умножением, умножать на обратный.
Путаешь. Вычитание можно заменить на сложение с обратным, но к делению это неприменимо.


Не путаю.
1. Можно погуглить на тему "замена целочисленного деления умножением" или глянуть Уоррена, например.
2. Можно посмотреть, во что преобразует деление на 10 любой сишный компилятор.
3. До кучи тут есть немного http://guildalfa.ru/alsha/node/31
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380105
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Midgard90,

внезапно переполнение:

val : int64;
n : integer;
n := val div 1000000000;
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380111
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Посмотрел во что мой код скомпилировался, похоже компилятор MSVС сам заменил деление
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
		itoa_d(buf, i);
000A1C42  mov         ecx,edi  
		itoa_d(buf, i);
000A1C44  mov         esi,0Ah  
000A1C49  nop         dword ptr [eax]  
000A1C50  mov         eax,0CCCCCCCDh  
000A1C55  dec         esi  
000A1C56  mul         eax,ecx  
000A1C58  shr         edx,3  
000A1C5B  mov         al,dl  
000A1C5D  shl         al,2  
000A1C60  lea         ebx,[eax+edx]  
000A1C63  add         bl,bl  
000A1C65  sub         cl,bl  
000A1C67  add         cl,30h  
000A1C6A  mov         byte ptr buf[esi],cl  
000A1C6E  mov         ecx,edx  
000A1C70  test        esi,esi  
000A1C72  jne         test+40h (0A1C50h)
		if(atoi(buf) != i) {
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380118
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
typedef uint32_t fix4_28;
void itoaZ(char *buf, uint64_t val)
{
	fix4_28 const f1_10000 = (1 << 28) / 10000;
	fix4_28 tmp0, tmp1, tmp2, tmp3;

	uint32_t v0 = val % 100000;
	uint32_t v1 = (val / 100000) % 100000;
	uint32_t v2 = (val / 10000000000) % 100000;;
	uint32_t v3 = (val / 1000000000000000) % 100000;;

	tmp0 = v0 * (f1_10000 + 1) - (v0 / 4);
	tmp1 = v1 * (f1_10000 + 1) - (v1 / 4);
	tmp2 = v2 * (f1_10000 + 1) - (v2 / 4);
	tmp3 = v3 * (f1_10000 + 1) - (v3 / 4);

	for (size_t i = 0; i < 5; i++)
	{
		buf[i + 0] = '0' + (char)(tmp3 >> 28);
		buf[i + 5] = '0' + (char)(tmp2 >> 28);
		buf[i + 10] = '0' + (char)(tmp1 >> 28);
		buf[i + 15] = '0' + (char)(tmp0 >> 28);
		tmp0 = (tmp0 & 0x0fffffff) * 10;
		tmp1 = (tmp1 & 0x0fffffff) * 10;
		tmp2 = (tmp2 & 0x0fffffff) * 10;
		tmp3 = (tmp3 & 0x0fffffff) * 10;
	}
}


аргумент беззнаковый, на выходе лидирующие нули
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380120
Midgard90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Странно) скорее всего старая дельфа топорная, потому как в их исходниках IntToStr похожий код с делением, что на выходе в асме не смотрел, но работает. В новой всё заменено уже сями и fastcall, да и работать в новой проще.

Насчёт переполнения думаю не проблема, разбить не на 2 а на 4 части и каждую прогнать, потом сложить PCharы. Конечный вариант кину после 9го) работы для праздников хватит))
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380222
Midgard90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
погонял. переполнения не встретил. приведи пример?
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380237
Пётр Седов
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr SharahovПётр СедовЕсли Delphi-йский компилятор для кода типа «n mod 10 ... n div 10» генерирует 2 инструкции div, то да :).
если немного подумать, а не переводить стрелки на компилятор, то легко понять, что есть алгоритм с одним делением:
d:=n div 10;
r:=n - d * 10;Деление, умножение, вычитание. Три операции в машинном коде. При том, что достаточно одной, потому что x86-инструкция div выдаёт 2 результата: частное (в регистре eax) и остаток (в регистре edx), и хороший оптимизирующий компилятор должен этим пользоваться. Ну это как для кода «cos(x) ... sin(x)» генерировать одну инструкцию fsincos. Или для кода «(n << 16) | (n >> 16)» генерировать одну инструкцию циклического сдвига. А так-то понятно, что руками много чего можно сделать, например беззнаковое деление на степень двойки заменить на битовый сдвиг.

Aleksandr SharahovПётр СедовМне удалось разбить только на 3 части.
если проявить смекалку, например, перейти к беззнаковым числам и поработать со старшими битами и с обратными числами, то удается получить 2 части.int64 (максимум 19 цифр) можно разбить на 3 части (9 цифр, 9 цифр, 1 цифра) или на 4 части (5 цифр, 5 цифр, 5 цифр, 4 цифры). А на 2 части как разбить? (чтобы каждая часть гарантированно влезала в 32 бита)

ИзопропилПётр Седов
Код: pascal
1.
2.
// если что-то осталось
      // дописываем вторую часть, чтобы было ровно 9 цифр


этого можно избежать изначально заполнив весь буфер нулямиДа, но длинные последовательности нулей редко встречаются, так что этим особо не ускоримся.

Изопропил
Код: plaintext
1.
void itoaZ(char *buf, uint64_t val)

Проверил, работает правильно. Пара мелочей:

Изопропил
Код: plaintext
1.
	uint32_t v3 = (val / 1000000000000000) % 100000;;

Похоже, не нужно.

Если работаем со строками в C-шном стиле, то хорошо бы писать завершающий нулевой char:
Код: plaintext
1.
2.
3.
4.
5.
void itoaZ(char *buf, uint64_t val)
{
	...
	buf[20] = '\0';
}


Я переписал ваш код на Pascal:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
type
  fix4_28 = cardinal;

const
  f1_10000: fix4_28 = (1 shl 28) div 10000;

function int32_to_text_fix(buf: pchar; buf_len: integer; value: integer): integer;
const
  buf2_len = 11; // самое длинное, что может быть -- это '-2147483648' = -pow(2, 31)
var
  buf2: array [0 .. buf2_len - 1] of char;
  abs_value: cardinal;
  v0, v1: cardinal;
  tmp0, tmp1: fix4_28;
  i, p, result_len: integer;
begin
  // считаем abs(value)
  if value >= 0 then begin
    abs_value := value;
  end else begin
    abs_value := cardinal(-(value + 1)) + 1; // единицы, чтобы не было переполнения, когда value = -2147483648
  end;

  // разбиваем число на 2 части, по 5 цифр в каждой
  v0 := abs_value mod 100000;
  v1 := abs_value div 100000;

  tmp0 := v0 * (f1_10000 + 1) - (v0 shr 2);
  tmp1 := v1 * (f1_10000 + 1) - (v1 shr 2);
  for i := 0 to 4 do begin
    buf2[i + 1] := chr(ord('0') + byte(tmp1 shr 28));
    buf2[i + 6] := chr(ord('0') + byte(tmp0 shr 28));
    tmp0 := (tmp0 and $0fffffff) * 10;
    tmp1 := (tmp1 and $0fffffff) * 10;
  end;

  // пропускаем начальные нули
  p := 1;
  while (p < 10) and (buf2[p] = '0') do begin
    inc(p);
  end;

  if value < 0 then begin
    dec(p);
    buf2[p] := '-';
  end;

  result_len := buf2_len - p;
  if result_len <= buf_len then begin
    move(buf2[p], buf^, result_len);
    result := result_len;
  end else begin
    result := -1;
  end;
end;

function int64_to_text_fix(buf: pchar; buf_len: integer; value: int64): integer;
const
  buf2_len = 20; // самое длинное, что может быть -- это '-9223372036854775808' = -pow(2, 63)
var
  buf2: array [0 .. buf2_len - 1] of char;
  abs_value: int64; // в Delphi 7 нет uint64
  v0, v1, v2, v3: cardinal;
  tmp0, tmp1, tmp2, tmp3: fix4_28;
  i, p, result_len: integer;
begin
  // считаем abs(value)
  if value >= 0 then begin
    abs_value := value;
  end else begin
    // hardcode для минимального значения, потому что не можем сделать '-value'
    if value = -9223372036854775807 - 1 then begin
      result_len := 20;
      if result_len <= buf_len then begin
        move('-9223372036854775808', buf^, result_len);
        result := result_len;
      end else begin
        result := -1;
      end;
      exit;
    end;
    abs_value := -value;
  end;

  // разбиваем число на 4 части, по 5 цифр в каждой
  v0 := abs_value mod 100000;
  v1 := (abs_value div 100000) mod 100000;
  v2 := (abs_value div 10000000000) mod 100000;
  v3 := abs_value div 1000000000000000;

  tmp0 := v0 * (f1_10000 + 1) - (v0 shr 2);
  tmp1 := v1 * (f1_10000 + 1) - (v1 shr 2);
  tmp2 := v2 * (f1_10000 + 1) - (v2 shr 2);
  tmp3 := v3 * (f1_10000 + 1) - (v3 shr 2);
  for i := 0 to 4 do begin
    buf2[i + 0] := chr(ord('0') + byte(tmp3 shr 28));
    buf2[i + 5] := chr(ord('0') + byte(tmp2 shr 28));
    buf2[i + 10] := chr(ord('0') + byte(tmp1 shr 28));
    buf2[i + 15] := chr(ord('0') + byte(tmp0 shr 28));
    tmp0 := (tmp0 and $0fffffff) * 10;
    tmp1 := (tmp1 and $0fffffff) * 10;
    tmp2 := (tmp2 and $0fffffff) * 10;
    tmp3 := (tmp3 and $0fffffff) * 10;
  end;

  // пропускаем начальные нули
  assert(buf2[0] = '0');
  p := 1;
  while (p < 19) and (buf2[p] = '0') do begin
    inc(p);
  end;

  if value < 0 then begin
    dec(p);
    buf2[p] := '-';
  end;

  result_len := buf2_len - p;
  if result_len <= buf_len then begin
    move(buf2[p], buf^, result_len);
    result := result_len;
  end else begin
    result := -1;
  end;
end;


Midgard90погонял. переполнения не встретил. приведи пример?Ваш код:
Midgard90
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
function Int64ToText(...; Value: Int64) ...
var
  ...
  Val : int64;
  ..., n, ...: integer;
begin
  ...
  val := value;
  ...
  n := val div 1000000000;
  ...
end;

Если value = val = 9223372036854775807 (максимальное значение), то:
val div 1000000000 = 9223372036 -- не влезает в integer (максимальное значение -- 2147483647). И в cardinal тоже не влезает (максимальное значение -- 4294967295).
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380247
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пётр СедовДеление, умножение, вычитание. Три операции в машинном коде. При том, что достаточно одной, потому что x86-инструкция div выдаёт 2 результата: частное (в регистре eax) и остаток (в регистре edx)

Ты же понимаешь, что мое замечание касалось ненужности второго деления в твоем коде?
И компилятор тут ни при чем.

На самом деле не нужно и первое деление. Или нужно, но другое.

Пётр Седовint64 (максимум 19 цифр) можно разбить на 3 части (9 цифр, 9 цифр, 1 цифра) или на 4 части (5 цифр, 5 цифр, 5 цифр, 4 цифры). А на 2 части как разбить? (чтобы каждая часть гарантированно влезала в 32 бита)


Идея в том, чтобы отделить часть работы,
которая формально соответствует очень маленькой третьей части данных,
и выполнить эту часть работы независимо от обработки двух других больших кусков.

Ну, когда, например, мы обрабатываем знак числа, мы же не называем это третьей частью данных,
хотя формально так оно и есть.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380262
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
void _ui64toa10( uint64_t const val,char *  obuf )
{
	uint32_t const f1_10001 = (1 << 28) / 10000 + 1;
	uint32_t tmp0, tmp1, tmp2, tmp3;
	char buf[21];

	uint64_t _vr,_vq;
	_vq = val / 10000000000;
	_vr = val % 10000000000;
	uint32_t v0 = _vr % 100000;
	uint32_t v1 = _vr / 100000;
	uint32_t v2 = _vq % 100000;
	uint32_t v3 = _vq / 100000;
	
	tmp0 = v0 * (f1_10001)-(v0 >> 2);
	tmp1 = v1 * (f1_10001)-(v1 >> 2);
	tmp2 = v2 * (f1_10001)-(v2 >> 2);
	tmp3 = v3 * (f1_10001)-(v3 >> 2);

	for (size_t i = 0; i < 5; i++) { buf[i     ] = '0' | (char)(tmp3 >> 28); tmp3 = (tmp3 & 0x0fffffff) * 10; }
	for (size_t i = 0; i < 5; i++) { buf[i + 5 ] = '0' | (char)(tmp2 >> 28); tmp2 = (tmp2 & 0x0fffffff) * 10; }
	for (size_t i = 0; i < 5; i++) { buf[i + 10] = '0' | (char)(tmp1 >> 28); tmp1 = (tmp1 & 0x0fffffff) * 10; }
	for (size_t i = 0; i < 5; i++) { buf[i + 15] = '0' | (char)(tmp0 >> 28); tmp0 = (tmp0 & 0x0fffffff) * 10; }

	buf[20] = 0;
	char  *p = buf;
	while (p < buf+19 && *p == '0')p++;
	memcpy(obuf, p, buf + 21 - p);
}



быстрее, чем библиотечный _ui64toa

глядя на ассемблерное порождение - сильно больше на 32 бит x86 - вряд ли получится выжать

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
; Function compile flags: /Ogtp
; File c:\vc\consoleapplication4\consoleapplication4\consoleapplication4.cpp
;	COMDAT ?_ui64toa10@@YAX_KPAD@Z
_TEXT	SEGMENT
_buf$ = -28						; size = 21
_obuf$1$ = -4						; size = 4
_val$ = 8						; size = 8
_tmp0$1$ = 12						; size = 4
_v2$1$ = 12						; size = 4
?_ui64toa10@@YAX_KPAD@Z PROC				; _ui64toa10, COMDAT
; _obuf$ = ecx

; 239  : {

	push	ebp
	mov	ebp, esp
	sub	esp, 28					; 0000001cH
	push	ebx
	push	esi
	push	edi
	push	ebx

; 240  : 	uint32_t const f1_10001 = (1 << 28) / 10000 + 1;
; 241  : 	uint32_t tmp0, tmp1, tmp2, tmp3;
; 242  : 	char buf[21];
; 243  : 
; 244  : 	uint64_t _vr,_vq;
; 245  : 	_vq = val / 10000000000;

	push	2
	push	1410065408				; 540be400H
	push	DWORD PTR _val$[ebp+4]
	mov	DWORD PTR _obuf$1$[ebp], ecx
	push	DWORD PTR _val$[ebp]
	call	__aulldvrm
	mov	esi, ebx
	pop	ebx

; 246  : 	_vr = val % 10000000000;
; 247  : 	uint32_t v0 = _vr % 100000;

	push	0
	push	100000					; 000186a0H
	mov	ebx, ecx
	mov	DWORD PTR _v2$1$[ebp], eax
	push	esi
	push	ebx
	mov	edi, edx
	call	__aulldiv

; 248  : 	uint32_t v1 = _vr / 100000;
; 249  : 	uint32_t v2 = _vq % 100000;

	push	0
	mov	esi, eax
	imul	ecx, esi, 100000
	push	100000					; 000186a0H
	push	edi
	mov	edi, DWORD PTR _v2$1$[ebp]
	push	edi
	sub	ebx, ecx
	call	__aulldiv
	imul	ecx, eax, 100000
	sub	edi, ecx

; 250  : 	uint32_t v3 = _vq / 100000;
; 251  : 	
; 252  : 	tmp0 = v0 * (f1_10001)-(v0 >> 2);

	imul	ecx, ebx, 26844
	shr	ebx, 2

; 253  : 	tmp1 = v1 * (f1_10001)-(v1 >> 2);
; 254  : 	tmp2 = v2 * (f1_10001)-(v2 >> 2);

	imul	edx, edi, 26844
	shr	edi, 2
	sub	ecx, ebx
	imul	ebx, esi, 26844
	mov	DWORD PTR _tmp0$1$[ebp], ecx

; 255  : 	tmp3 = v3 * (f1_10001)-(v3 >> 2);

	imul	ecx, eax, 26844
	sub	edx, edi
	shr	eax, 2
	shr	esi, 2
	sub	ebx, esi
	sub	ecx, eax

; 256  : 
; 257  : 	for (size_t i = 0; i < 5; i++) { buf[i     ] = '0' | (char)(tmp3 >> 28); tmp3 = (tmp3 & 0x0fffffff) * 10; }

	mov	eax, ecx
	and	ecx, 268435455				; 0fffffffH
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp], al
	lea	ecx, DWORD PTR [ecx+ecx*4]
	add	ecx, ecx
	mov	eax, ecx
	and	ecx, 268435455				; 0fffffffH
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+1], al
	lea	ecx, DWORD PTR [ecx+ecx*4]
	add	ecx, ecx
	mov	eax, ecx
	and	ecx, 268435455				; 0fffffffH
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+2], al
	lea	ecx, DWORD PTR [ecx+ecx*4]
	add	ecx, ecx
	mov	eax, ecx
	and	ecx, 268435455				; 0fffffffH
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+3], al
	lea	eax, DWORD PTR [ecx+ecx*4]
	add	eax, eax
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+4], al

; 258  : 	for (size_t i = 0; i < 5; i++) { buf[i + 5 ] = '0' | (char)(tmp2 >> 28); tmp2 = (tmp2 & 0x0fffffff) * 10; }

	mov	eax, edx
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	and	edx, 268435455				; 0fffffffH
	mov	BYTE PTR _buf$[ebp+5], al
	lea	ecx, DWORD PTR [edx+edx*4]
	add	ecx, ecx
	mov	eax, ecx
	and	ecx, 268435455				; 0fffffffH
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+6], al
	lea	ecx, DWORD PTR [ecx+ecx*4]
	add	ecx, ecx
	mov	eax, ecx
	and	ecx, 268435455				; 0fffffffH
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+7], al
	lea	ecx, DWORD PTR [ecx+ecx*4]
	add	ecx, ecx
	mov	eax, ecx
	and	ecx, 268435455				; 0fffffffH
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+8], al
	lea	eax, DWORD PTR [ecx+ecx*4]
	add	eax, eax
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+9], al

; 259  : 	for (size_t i = 0; i < 5; i++) { buf[i + 10] = '0' | (char)(tmp1 >> 28); tmp1 = (tmp1 & 0x0fffffff) * 10; }

	mov	eax, ebx
	shr	eax, 28					; 0000001cH
	and	ebx, 268435455				; 0fffffffH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+10], al
	lea	ecx, DWORD PTR [ebx+ebx*4]
	add	ecx, ecx
	mov	eax, ecx
	and	ecx, 268435455				; 0fffffffH
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+11], al
	lea	ecx, DWORD PTR [ecx+ecx*4]
	add	ecx, ecx
	mov	eax, ecx
	and	ecx, 268435455				; 0fffffffH
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+12], al
	lea	ecx, DWORD PTR [ecx+ecx*4]
	add	ecx, ecx
	mov	eax, ecx
	and	ecx, 268435455				; 0fffffffH
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+13], al
	lea	eax, DWORD PTR [ecx+ecx*4]

; 260  : 	for (size_t i = 0; i < 5; i++) { buf[i + 15] = '0' | (char)(tmp0 >> 28); tmp0 = (tmp0 & 0x0fffffff) * 10; }

	mov	ecx, DWORD PTR _tmp0$1$[ebp]
	add	eax, eax
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+14], al
	mov	eax, ecx
	and	ecx, 268435455				; 0fffffffH
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+15], al
	lea	ecx, DWORD PTR [ecx+ecx*4]
	add	ecx, ecx
	mov	eax, ecx
	and	ecx, 268435455				; 0fffffffH
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+16], al
	lea	ecx, DWORD PTR [ecx+ecx*4]
	add	ecx, ecx
	mov	eax, ecx
	and	ecx, 268435455				; 0fffffffH
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+17], al
	lea	ecx, DWORD PTR [ecx+ecx*4]
	add	ecx, ecx
	mov	eax, ecx
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H

; 261  : 
; 262  : 	buf[20] = 0;

	mov	BYTE PTR _buf$[ebp+20], 0
	and	ecx, 268435455				; 0fffffffH
	mov	BYTE PTR _buf$[ebp+18], al
	lea	eax, DWORD PTR [ecx+ecx*4]
	add	eax, eax
	shr	eax, 28					; 0000001cH
	or	al, 48					; 00000030H
	mov	BYTE PTR _buf$[ebp+19], al

; 263  : 	char  *p = buf;

	lea	eax, DWORD PTR _buf$[ebp]
$LL14@ui64toa10:

; 264  : 	while (p < buf+19 && *p == '0')p++;

	cmp	BYTE PTR [eax], 48			; 00000030H
	jne	SHORT $LN15@ui64toa10
	inc	eax
	lea	ecx, DWORD PTR _buf$[ebp+19]
	cmp	eax, ecx
	jb	SHORT $LL14@ui64toa10
$LN15@ui64toa10:

; 265  : 	memcpy(obuf, p, buf + 21 - p);

	lea	ecx, DWORD PTR _buf$[ebp+21]
	sub	ecx, eax
	push	ecx
	push	eax
	push	DWORD PTR _obuf$1$[ebp]
	call	_memcpy
	add	esp, 12					; 0000000cH

; 266  : }

	pop	edi
	pop	esi
	pop	ebx
	mov	esp, ebp
	pop	ebp
	ret	0
?_ui64toa10@@YAX_KPAD@Z ENDP				; _ui64toa10
_TEXT	ENDS

...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380388
__Avenger__
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Еще можно itoa и atoi собрать в obj файл на с++ и затем использовать в проекте на Delphi.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380392
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
__Avenger__в obj файл на с++
что означает этот набор слов?
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380394
__Avenger__
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Изопропил__Avenger__в obj файл на с++
что означает этот набор слов?

Скомпилировать c++ файл в obj файл, а затем использовать его в delphi через директиву {$LINK *.obj}. ZLib так например в дельфи подключен.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380395
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
__Avenger__Изопропилпропущено...

что означает этот набор слов?

Скомпилировать c++ файл в obj файл, а затем использовать его в delphi через директиву {$LINK *.obj}. ZLib так например в дельфи подключен.
ну лучше С, а не С++ и внимательно следить чтоб из рантайма лишку не зацепить.

проще dll сделать
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380416
Пётр Седов
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Изопропил
Код: plaintext
1.
2.
3.
4.
	uint64_t ...,_vq;
	...
	uint32_t v2 = _vq % 100000;
	uint32_t v3 = _vq / 100000;

Код: sql
1.
2.
3.
4.
5.
6.
7.
; 249  : 	uint32_t v2 = _vq % 100000;
	...
	call	__aulldiv
	imul	ecx, eax, 100000
	sub	edi, ecx

; 250  : 	uint32_t v3 = _vq / 100000;

Visual C++ сгенерировал такой код, как предлагал Aleksandr Sharahov: деление, умножение, вычитание. Хотя вполне мог сгенерировать код типа:
edx:eax ← _vq
div 100000
v3 ← eax (частное)
v2 ← edx (остаток)

Изопропил
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
	tmp0 = v0 * (f1_10001)-(v0 >> 2);
	tmp1 = v1 * (f1_10001)-(v1 >> 2);
	tmp2 = v2 * (f1_10001)-(v2 >> 2);
	tmp3 = v3 * (f1_10001)-(v3 >> 2);

	for (size_t i = 0; i < 5; i++) { buf[i     ] = '0' | (char)(tmp3 >> 28); tmp3 = (tmp3 & 0x0fffffff) * 10; }
	for (size_t i = 0; i < 5; i++) { buf[i + 5 ] = '0' | (char)(tmp2 >> 28); tmp2 = (tmp2 & 0x0fffffff) * 10; }
	for (size_t i = 0; i < 5; i++) { buf[i + 10] = '0' | (char)(tmp1 >> 28); tmp1 = (tmp1 & 0x0fffffff) * 10; }
	for (size_t i = 0; i < 5; i++) { buf[i + 15] = '0' | (char)(tmp0 >> 28); tmp0 = (tmp0 & 0x0fffffff) * 10; }

...
глядя на ассемблерное порождение - сильно больше на 32 бит x86 - вряд ли получится выжатьТут одинаковые операции делаются, поэтому если писать на ассемблере, то можно попытаться ускориться за счёт использования MMX-инструкций, которые работают с 32-битными числами, упакованными в 64-битные регистры:
paddd -- делает параллельно два сложения
psubd -- делает параллельно два вычитания
pslld -- делает параллельно два битовых сдвига влево
psrld -- делает параллельно два битовых сдвига вправо
pand -- делает параллельно два битовых and (на самом деле, просто 64-битный and)
А вот умножения нормального нет, поэтому умножение на f1_10001 придётся делать обычными инструкциями, а умножение на 10 заменить на «(n << 3) + (n << 1)». Кстати, Visual C++ умножение на 10 делает так:
Изопропил
Код: sql
1.
2.
	lea	ecx, DWORD PTR [ecx+ecx*4]
	add	ecx, ecx


Изопропил
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
; 263  : 	char  *p = buf;

	lea	eax, DWORD PTR _buf$[ebp]
$LL14@ui64toa10:

; 264  : 	while (p < buf+19 && *p == '0')p++;

	cmp	BYTE PTR [eax], 48			; 00000030H
	jne	SHORT $LN15@ui64toa10
	inc	eax
	lea	ecx, DWORD PTR _buf$[ebp+19]
	cmp	eax, ecx
	jb	SHORT $LL14@ui64toa10
$LN15@ui64toa10:

Зачем Visual C++ делает «ecx ← buf + 19» на каждой итерации цикла? Это достаточно сделать один раз до цикла. А если писать на ассемблере самому, то тут вообще, насколько я понимаю, весь цикл можно записать одной инструкцией: repe scasb.

Изопропилбыстрее, чем библиотечный _ui64toaКруто.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380424
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пётр СедовVisual C++ сгенерировал такой код, как предлагал Aleksandr Sharahov: деление, умножение, вычитание. Хотя вполне мог сгенерировать код типа:
edx:eax ← _vq
div 100000
v3 ← eax (частное)
v2 ← edx (остаток)

uint64/uint32 - частное может не уместиться в uint32,
потому нельзя одной командой, переполнение может случиться - потому - деление "столбиком"
(см исходники ulldiv.asm, ulldrvm.asm)

Пётр СедовЗачем Visual C++ делает «ecx ← buf + 19» на каждой итерации цикла?
inc и lea выполнятся одновременно, потери производительности не будет, хотя действительно странно.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380429
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SSE2 версия
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
static inline __m128i _mm_mullo_epi32emu(const __m128i &a, const __m128i &b)
{
	__m128i tmp1 = _mm_mul_epu32(a, b); /* mul 2,0*/
	__m128i tmp2 = _mm_mul_epu32(_mm_srli_si128(a, 4), _mm_srli_si128(b, 4)); /* mul 3,1 */
	return _mm_unpacklo_epi32(_mm_shuffle_epi32(tmp1, _MM_SHUFFLE(0, 0, 2, 0)), _mm_shuffle_epi32(tmp2, _MM_SHUFFLE(0, 0, 2, 0))); /* shuffle results to [63..0] and pack */
}

void _ui64toa10m(const uint64_t  val, char *obuf )
{
	int32_t const f1_10001 = (1 << 28) / 10000 + 1;

	uint64_t _vr, _vq;
	_vq = val / 10000000000;
	_vr = val % 10000000000;
	uint32_t v0 = _vr % 100000;
	uint32_t v1 = _vr / 100000;
	uint32_t v2 = _vq % 100000;
	uint32_t v3 = _vq / 100000;

	char buf[21];
	__m128i v = _mm_set_epi32(v3, v2, v1, v0);
	__m128i tmp = _mm_sub_epi32(_mm_mullo_epi32emu(v, _mm_set1_epi32(f1_10001)), _mm_srai_epi32(v, 2));

	for (size_t i = 0; i < 5; i++)
	{
		__m128i tmps = _mm_add_epi32(_mm_and_si128(_mm_srai_epi32(tmp, 28), _mm_set1_epi32(0xf)), _mm_set1_epi32('0'));
		buf[i + 0] = tmps.m128i_u32[3];
		buf[i + 5] = tmps.m128i_u32[2];
		buf[i + 10] = tmps.m128i_u32[1];
		buf[i + 15] = tmps.m128i_u32[0];
		tmp = _mm_mullo_epi32emu(_mm_and_si128(tmp, _mm_set1_epi32(0x0fffffff)), _mm_set1_epi32(10));
	}
	buf[20] = 0;
	char  *p = buf;
	while (p < buf + 19 && *p == '0')p++;
	memcpy(obuf, p, buf + 21 - p);
}



не ускоряет ( по крайней мере на тех CPU, что использовал)

а что до "крутости" - _ui64toa работает с произвольным основанием системы счисления и скорость зависит от количества цифр, возня может иметь смысл, если часто преобразовывать большие числа приходится.

в конечном итоге - преобразование двоичного в десятичное вряд ли является узким местом.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380430
Siemargl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если нужно обрабатывать многоцифр, то нужно их обрабатывать параллельно.

И добро пожаловать в волшебный мир SIMD.

Остальное пырхание не стоит внимания.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380435
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SiemarglИ добро пожаловать в волшебный мир SIMD.
он не настолько волшебен, как иногда кажется.

ускорь _ui64toa с помощью SSE4, а там и будем выводы делать
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380486
Midgard90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Таак... с переполнением разобрался, потом нашёл ещё несколько багов: прошлая функция не дописывала нули, например:
9000000001000000001 на выходе 911)). В общем вышло как-то так:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
function Int64ToText(Arg: PAnsiChar; Value: Int64):integer; assembler;
var
  Ch1,Ch2,Ch3,Ch1_old,Ch2_old : array [0..10] of Char;
  PCh1,Pch2,PCh3,PCh1_old,PCh2_old :PAnsiChar;
  Val,n : int64;
  count, part : integer;
begin
  Ch1 := ('');
  Ch2 := ('');
  Ch3 := ('');
  Ch1_old := ('');
  Ch2_old := ('');
  val := value;
  PCh1 := @Ch1;
  PCh2 := @Ch2;
  PCh3 := @Ch3;
  PCh1_old := @Ch1_old;
  PCh2_old := @Ch2_old;
  Result := 0;
  if val < 0 then begin
    StrCopy(Arg,'-');
  end;
  //первая часть
  if (val <> 0) then begin
    part := val mod 1000000000;
    n := val div 1000000000;
    Count := IntToText(PCh1,abs(part));
    if (abs(part) < 1000000000)and(n<>0) then begin
      while Count < 9 do begin
        StrCopy(StrEnd(Pch1_old), '0');
        Inc(Count);
      end;
    end;
    StrCopy(StrEnd(Pch1_old), PCh1);

    Result := Result + Count;
  end;
  //Вторая часть
  if n <> 0 then begin
    part := n mod 1000000000;
    n := n div 1000000000;
    Count := IntToText(PCh2,abs(part));
    if (abs(part) < 1000000000)and(n<>0) then begin
      while Count < 9 do begin
        StrCopy(StrEnd(Pch2_old), '0');
        Inc(Count);
      end;
    end;
    StrCopy(StrEnd(Pch2_old), PCh2);
    Result := Result + Count;
  end;
  //третья часть
  if n <> 0 then begin
  part := n mod 10;
  Count := IntToText(PCh3,abs(part));
  Result := Result + Count;
  end;
  StrCopy(StrEnd(Arg), PCh3);
  StrCopy(StrEnd(Arg), PCh2_old);
  StrCopy(StrEnd(Arg), PCh1_old);

end;


Не думаю теперь что она быстрее IntToStr или предложенной ранее чисто дельфийской)) Завтра на скорость будем тестить с профитом по ядрам. Как её укоротить или оптимизировать уже что-то не соображаю. И масса локальных напрягает. Не функция а костыль, но работает)) Если есть мысли, с удовольствием выслушаю, как её сделать красивши)
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380488
Midgard90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Да, забыл, IntToText() осталась прежней, чисто асемблер, как и кидал ранее.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380516
Siemargl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ИзопропилSiemarglИ добро пожаловать в волшебный мир SIMD.
он не настолько волшебен, как иногда кажется.

ускорь _ui64toa с помощью SSE4, а там и будем выводы делатьТы не понял идею. Нужно не одно число переводить, а пачку.
Т.е. _ui64toa(const uint64_t val[4],.....)
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380523
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Siemargl,

у интела в SIMD уже появилось деление 64 битных целых ?
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380588
Siemargl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ИзопропилSiemargl,

у интела в SIMD уже появилось деление 64 битных целых ?Эмм, не нашел. Там простого деления я совсем не наблюдаю, только алгоритмами.

Но оно не требуется. Нам нужно деление с остатком 64бит на 32бит.
Оно реализуется 32-битными операциями, например так div_s64_rem()
http://lxr.free-electrons.com/source/lib/div64.c
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380591
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SiemarglНам нужно деление с остатком 64бит на 32бит.
Оно реализуется 32-битными операциями, например так div_s64_rem()
а SIMD каким боком тогда?
вариация на тему _ui64toa получается

что касается реализации деления в msvc для x86 -

деление - _aulldvrm (msvc runtime, ветка для короткого делителя нас интересует)
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
        title   ulldvrm - unsigned long divide and remainder routine
;***
;ulldvrm.asm - unsigned long divide and remainder routine
;
;       Copyright (c) Microsoft Corporation. All rights reserved.
;
;Purpose:
;       defines the unsigned long divide and remainder routine
;           __aulldvrm
;
;*******************************************************************************


.xlist
include vcruntime.inc
include mm.inc
.list

;***
;ulldvrm - unsigned long divide and remainder
;
;Purpose:
;       Does a unsigned long divide and remainder of the arguments.  Arguments
;       are not changed.
;
;Entry:
;       Arguments are passed on the stack:
;               1st pushed: divisor (QWORD)
;               2nd pushed: dividend (QWORD)
;
;Exit:
;       EDX:EAX contains the quotient (dividend/divisor)
;       EBX:ECX contains the remainder (divided % divisor)
;       NOTE: this routine removes the parameters from the stack.
;
;Uses:
;       ECX
;
;Exceptions:
;
;*******************************************************************************

        CODESEG

_aulldvrm PROC NEAR
.FPO (1, 4, 0, 0, 0, 0)

        push    esi

; Set up the local stack and save the index registers.  When this is done
; the stack frame will look as follows (assuming that the expression a/b will
; generate a call to aulldvrm(a, b)):
;
;               -----------------
;               |               |
;               |---------------|
;               |               |
;               |--divisor (b)--|
;               |               |
;               |---------------|
;               |               |
;               |--dividend (a)-|
;               |               |
;               |---------------|
;               | return addr** |
;               |---------------|
;       ESP---->|      ESI      |
;               -----------------
;

DVND    equ     [esp + 8]       ; stack address of dividend (a)
DVSR    equ     [esp + 16]      ; stack address of divisor (b)

;
; Now do the divide.  First look to see if the divisor is less than 4194304K.
; If so, then we can use a simple algorithm with word divides, otherwise
; things get a little more complex.
;

        mov     eax,HIWORD(DVSR) ; check to see if divisor < 4194304K
        or      eax,eax
        jnz     short L1        ; nope, gotta do this the hard way
        mov     ecx,LOWORD(DVSR) ; load divisor
        mov     eax,HIWORD(DVND) ; load high word of dividend
        xor     edx,edx
        div     ecx             ; get high order bits of quotient
        mov     ebx,eax         ; save high bits of quotient
        mov     eax,LOWORD(DVND) ; edx:eax <- remainder:lo word of dividend
        div     ecx             ; get low order bits of quotient
        mov     esi,eax         ; ebx:esi <- quotient

;
; Now we need to do a multiply so that we can compute the remainder.
;
        mov     eax,ebx         ; set up high word of quotient
        mul     dword ptr LOWORD(DVSR) ; HIWORD(QUOT) * DVSR
        mov     ecx,eax         ; save the result in ecx
        mov     eax,esi         ; set up low word of quotient
        mul     dword ptr LOWORD(DVSR) ; LOWORD(QUOT) * DVSR
        add     edx,ecx         ; EDX:EAX = QUOT * DVSR
        jmp     short L2        ; complete remainder calculation

;
; Here we do it the hard way.  Remember, eax contains DVSRHI
;

L1:
        mov     ecx,eax         ; ecx:ebx <- divisor
        mov     ebx,LOWORD(DVSR)
        mov     edx,HIWORD(DVND) ; edx:eax <- dividend
        mov     eax,LOWORD(DVND)
L3:
        shr     ecx,1           ; shift divisor right one bit; hi bit <- 0
        rcr     ebx,1
        shr     edx,1           ; shift dividend right one bit; hi bit <- 0
        rcr     eax,1
        or      ecx,ecx
        jnz     short L3        ; loop until divisor < 4194304K
        div     ebx             ; now divide, ignore remainder
        mov     esi,eax         ; save quotient

;
; We may be off by one, so to check, we will multiply the quotient
; by the divisor and check the result against the orignal dividend
; Note that we must also check for overflow, which can occur if the
; dividend is close to 2**64 and the quotient is off by 1.
;

        mul     dword ptr HIWORD(DVSR) ; QUOT * HIWORD(DVSR)
        mov     ecx,eax
        mov     eax,LOWORD(DVSR)
        mul     esi             ; QUOT * LOWORD(DVSR)
        add     edx,ecx         ; EDX:EAX = QUOT * DVSR
        jc      short L4        ; carry means Quotient is off by 1

;
; do long compare here between original dividend and the result of the
; multiply in edx:eax.  If original is larger or equal, we are ok, otherwise
; subtract one (1) from the quotient.
;

        cmp     edx,HIWORD(DVND) ; compare hi words of result and original
        ja      short L4        ; if result > original, do subtract
        jb      short L5        ; if result < original, we are ok
        cmp     eax,LOWORD(DVND) ; hi words are equal, compare lo words
        jbe     short L5        ; if less or equal we are ok, else subtract
L4:
        dec     esi             ; subtract 1 from quotient
        sub     eax,LOWORD(DVSR) ; subtract divisor from result
        sbb     edx,HIWORD(DVSR)
L5:
        xor     ebx,ebx         ; ebx:esi <- quotient

L2:
;
; Calculate remainder by subtracting the result from the original dividend.
; Since the result is already in a register, we will do the subtract in the
; opposite direction and negate the result.
;

        sub     eax,LOWORD(DVND) ; subtract dividend from result
        sbb     edx,HIWORD(DVND)
        neg     edx             ; otherwise, negate the result
        neg     eax
        sbb     edx,0

;
; Now we need to get the quotient into edx:eax and the remainder into ebx:ecx.
;
        mov     ecx,edx
        mov     edx,ebx
        mov     ebx,ecx
        mov     ecx,eax
        mov     eax,esi
;
; Just the cleanup left to do.  edx:eax contains the quotient.
; Restore the saved registers and return.
;

        pop     esi

        ret     16

_aulldvrm ENDP

        end



и _aulldiv (остаток восстанавливать в вызывающем коде)
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
        title   ulldiv - unsigned long divide routine
;***
;ulldiv.asm - unsigned long divide routine
;
;       Copyright (c) Microsoft Corporation. All rights reserved.
;
;Purpose:
;       defines the unsigned long divide routine
;           __aulldiv
;
;*******************************************************************************


.xlist
include vcruntime.inc
include mm.inc
.list

;***
;ulldiv - unsigned long divide
;
;Purpose:
;       Does a unsigned long divide of the arguments.  Arguments are
;       not changed.
;
;Entry:
;       Arguments are passed on the stack:
;               1st pushed: divisor (QWORD)
;               2nd pushed: dividend (QWORD)
;
;Exit:
;       EDX:EAX contains the quotient (dividend/divisor)
;       NOTE: this routine removes the parameters from the stack.
;
;Uses:
;       ECX
;
;Exceptions:
;
;*******************************************************************************

        CODESEG

_aulldiv        PROC NEAR
.FPO (2, 4, 0, 0, 0, 0)

        push    ebx
        push    esi

; Set up the local stack and save the index registers.  When this is done
; the stack frame will look as follows (assuming that the expression a/b will
; generate a call to uldiv(a, b)):
;
;               -----------------
;               |               |
;               |---------------|
;               |               |
;               |--divisor (b)--|
;               |               |
;               |---------------|
;               |               |
;               |--dividend (a)-|
;               |               |
;               |---------------|
;               | return addr** |
;               |---------------|
;               |      EBX      |
;               |---------------|
;       ESP---->|      ESI      |
;               -----------------
;

DVND    equ     [esp + 12]      ; stack address of dividend (a)
DVSR    equ     [esp + 20]      ; stack address of divisor (b)

;
; Now do the divide.  First look to see if the divisor is less than 4194304K.
; If so, then we can use a simple algorithm with word divides, otherwise
; things get a little more complex.
;

        mov     eax,HIWORD(DVSR) ; check to see if divisor < 4194304K
        or      eax,eax
        jnz     short L1        ; nope, gotta do this the hard way
        mov     ecx,LOWORD(DVSR) ; load divisor
        mov     eax,HIWORD(DVND) ; load high word of dividend
        xor     edx,edx
        div     ecx             ; get high order bits of quotient
        mov     ebx,eax         ; save high bits of quotient
        mov     eax,LOWORD(DVND) ; edx:eax <- remainder:lo word of dividend
        div     ecx             ; get low order bits of quotient
        mov     edx,ebx         ; edx:eax <- quotient hi:quotient lo
        jmp     short L2        ; restore stack and return

;
; Here we do it the hard way.  Remember, eax contains DVSRHI
;

L1:
        mov     ecx,eax         ; ecx:ebx <- divisor
        mov     ebx,LOWORD(DVSR)
        mov     edx,HIWORD(DVND) ; edx:eax <- dividend
        mov     eax,LOWORD(DVND)
L3:
        shr     ecx,1           ; shift divisor right one bit; hi bit <- 0
        rcr     ebx,1
        shr     edx,1           ; shift dividend right one bit; hi bit <- 0
        rcr     eax,1
        or      ecx,ecx
        jnz     short L3        ; loop until divisor < 4194304K
        div     ebx             ; now divide, ignore remainder
        mov     esi,eax         ; save quotient

;
; We may be off by one, so to check, we will multiply the quotient
; by the divisor and check the result against the orignal dividend
; Note that we must also check for overflow, which can occur if the
; dividend is close to 2**64 and the quotient is off by 1.
;

        mul     dword ptr HIWORD(DVSR) ; QUOT * HIWORD(DVSR)
        mov     ecx,eax
        mov     eax,LOWORD(DVSR)
        mul     esi             ; QUOT * LOWORD(DVSR)
        add     edx,ecx         ; EDX:EAX = QUOT * DVSR
        jc      short L4        ; carry means Quotient is off by 1

;
; do long compare here between original dividend and the result of the
; multiply in edx:eax.  If original is larger or equal, we are ok, otherwise
; subtract one (1) from the quotient.
;

        cmp     edx,HIWORD(DVND) ; compare hi words of result and original
        ja      short L4        ; if result > original, do subtract
        jb      short L5        ; if result < original, we are ok
        cmp     eax,LOWORD(DVND) ; hi words are equal, compare lo words
        jbe     short L5        ; if less or equal we are ok, else subtract
L4:
        dec     esi             ; subtract 1 from quotient
L5:
        xor     edx,edx         ; edx:eax <- quotient
        mov     eax,esi

;
; Just the cleanup left to do.  edx:eax contains the quotient.
; Restore the saved registers and return.
;

L2:

        pop     esi
        pop     ebx

        ret     16

_aulldiv        ENDP

        end



беда в том,что самая затратная операция в алгориме - деление
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380624
Пётр Седов
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ИзопропилПётр СедовVisual C++ сгенерировал такой код, как предлагал Aleksandr Sharahov: деление, умножение, вычитание. Хотя вполне мог сгенерировать код типа:
edx:eax ← _vq
div 100000
v3 ← eax (частное)
v2 ← edx (остаток)

uint64/uint32 - частное может не уместиться в uint32,Да, но в коде:
Изопропил
Код: plaintext
1.
2.
3.
4.
	uint64_t ...,_vq;
	...
	uint32_t v2 = _vq % 100000;
	uint32_t v3 = _vq / 100000;

нужно не всё частное, а только младшие 32 бита этого частного.

Изопропилпотому нельзя одной командой, переполнение может случитьсяЯ тут почитал, оказывается инструкция div возбуждает исключение в случае переполнения (если частное не влезает в 32 бита), вот поэтому компилятор и не может её здесь использовать. Но если заверить компилятор, что частное влезает в 32 бита:
Код: plaintext
1.
2.
__assume(_vq / 100000 <= UINT32_MAX); // для Visual C++
uint32_t v3 = _vq / 100000;

то может и будет использовать.
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380754
Siemargl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ИзопропилSiemarglНам нужно деление с остатком 64бит на 32бит.
Оно реализуется 32-битными операциями, например так div_s64_rem()
а SIMD каким боком тогда?
вариация на тему _ui64toa получается
Нет. Пусть у нас есть функция func(x:64-bit), и мы ее будем вызывать для многих x, foreach(x: {}) func(x).
Тогда векторизация ее выглядит так
func(x[4])
{
все операции с x заменяются на соответствующие векторные с x[4]
}

Конечно, это придется сделать со всеми вызываемыми функциями из func(), в том числе, заменить intrinsic деления, что весьма муторно.

В общем, вот желающим матаппарат, а для меня задача имеет решение,
но подробности в данный момент неинтересны, и просто пое-ься я тоже не хочу =)
...
Рейтинг: 0 / 0
IntToPChar на ASM для DELPHI
    #39380776
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SiemarglВ общем, вот желающим матаппарат, а для меня задача имеет решение,
для меня тоже имеет решение, беда в том что оно весьма вероятно окажется менее производительным,
чем со скалярными операциями.

ну а попытка сегодня писать подобное на x86, а не на x64 - странна как то.

А написанное выше - просто разминка для мозга и повод освежить в памяти систему команд интела и интринсики
Интел
...
Рейтинг: 0 / 0
54 сообщений из 54, показаны все 3 страниц
Форумы / Программирование [игнор отключен] [закрыт для гостей] / IntToPChar на ASM для DELPHI
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]