[uquote="COKPOWEHEU",url="/forum/viewtopic.php?p=4109105#p4109105"]Откуда-то два деления придумали, "лишние" обращения, "лишние" ветвления.[/uquote]Эта операция "/" - называется деление, ежли что. И здесь "%" - тоже деление.
А вот это "buf[0]" - лишние обращения к памяти, коих тоже зачем-то вагон.
Это если вы в своём же коде разобраться не можете.
И на кой два раза одно и то же действие делать???
[uquote="COKPOWEHEU",url="/forum/viewtopic.php?p=4109105#p4109105"]Впрочем, ладно: если уж взяли мой пример для произвольного основания, продемонстрируйте свой вариант, решающий эту задачу не хуже. Естественно, в качестве доказательства принимается количество тактов для среднего и худшего случаев в сравнении с тем, что на вашем компиляторе выдает мой "неоптимальный" код.[/uquote]Например:
Код: Выделить всё
//Преобразует число x в строку в str с основанием 10.
//Возвращает указатель на завершающий 0-ой символ.
char * utoa10(u32 x, char *str)
{
#define D ((B35 + 5) / 10)
u32 i;
char *s1, *s = str;
do {
x = (i = x) * D >> 35;
*s++ = i - x * 10 + '0';
} while (x);
*(s1 = s) = 0;
while ((uint)--s > (uint)str) {
i = *s;
*s = *str;
*str++ = i;
}
return s1;
#undef D
}
Собственно - преобразование в строку - это 1-й цикл. Как видно в нём: 1) ни одного деления; 2) ни одного ветвления; 3) и ни одного лишнего обращения к памяти.
Количество тактов считайте сами, вот листинг:
Код: Выделить всё
char * utoa10(u32 x, char *str)
{
_Z6utoa10mPc: (+1)
0xB470 PUSH {R4-R6}
0x460C MOV R4,R1
#define D ((B35 + 5) / 10)
u32 i;
char *s1, *s = str;
REQUIRE ?Subroutine0
;; // Fall through to label ?Subroutine0
?Subroutine0: (+1)
0x4625 MOV R5,R4
0x.... 0x.... LDR.W R6,??DataTable4 ;; 0xcccccccd
??Subroutine0_0: (+1)
0x4602 MOV R2,R0
0xFBA6 0x0102 UMULL R0,R1,R6,R2
0x08C8 LSRS R0,R1,#+3
0xEB00 0x0380 ADD R3,R0,R0, LSL #+2
0xEBA2 0x0143 SUB R1,R2,R3, LSL #+1
0x3130 ADDS R1,R1,#+48
0x2800 CMP R0,#+0
0xF805 0x1B01 STRB R1,[R5], #+1
0xD1F2 BNE.N ??Subroutine0_0
0x4628 MOV R0,R5
0x2100 MOVS R1,#+0
0x7001 STRB R1,[R0, #+0]
0xE004 B.N ??Subroutine0_1
??Subroutine0_2: (+1)
0x7829 LDRB R1,[R5, #+0]
0x7822 LDRB R2,[R4, #+0]
0x702A STRB R2,[R5, #+0]
0xF804 0x1B01 STRB R1,[R4], #+1
??Subroutine0_1: (+1)
0x1E6D SUBS R5,R5,#+1
0x42AC CMP R4,R5
0xD3F7 BCC.N ??Subroutine0_2
0xBC70 POP {R4-R6}
0x4770 BX LR ;; return
Итого = 8 команд внутри цикла (1-й цикл), почти все 1-тактные (кроме единственной STRB), ни одного ветвления, ни одного деления.
И конечно компилятор тут ступил, ибо одна команда в этом цикле явно лишняя, и если бы у него было больше мозгов, он бы её удалил и было бы ещё быстрее.
Такты в своём творении сами считайте.
Добавлено after 2 minutes 48 seconds:
[uquote="AVI-crak",url="/forum/viewtopic.php?p=4109233#p4109233"]Свежий GCC оптимизирует подобный алгоритм по самые гланды. Он даже умножение заменяет сложениями и сдвигами, на проце с аппаратной математикой двойной точности!!![/uquote]Бред какой-то... Это деление должно заменяться
умным программистом на
одно однотактное умножение. А не на кучу сложений/сдвигов тупым компилятором. Никогда компилятор не будет умнее умного программиста. Даже в этом листинге видно, что даже на максимальной оптимизации компилятор впендюрил как минимум одну лишнюю команду. Непонятно зачем. Это IAR. Может GCC лучше скомпилит - не пробовал.