jcxz писал(а):Зачем для каждого разряда 2 деления? Деление - плохая операция. Даже на тех МК, где она аппаратно есть, выполняется очень медленно.
Потому что это:
- легко читается и понимается
- может оптимизироваться компилятором так, что делений там и не будет.
- такой код вполне уместен при выводе результатов на экран, будучи вызываем один раз в главном цикле. В критичных местах вроде прерываний - да, делить лучше не стоит.
Например, анализируя листинг, я как-то видел что деление 8-битного числа 'num' на константу 10 компилятор обычно преобразует в умножение на 205 и сдвиг:
Код: Выделить всё
// Например, num = 234
// Написано программистом
digit = num % 10 # digit = 234 % 10 = 4
num /=10 # num = 234 / 10 = 23
// Оптимизировано компилятором
tmp = (num * 205) << 11; # tmp = (234 * 205) << 11 = 47970 << 11 = 23
digit = num - tpm * 10; # digit = 234 - 23 * 10 = 234 - 230 = 4
num = tmp; # num = 23
Что за магическое число 205?
Деление числа на 10 равносильно его умножению на 204.8 и последующему делению на 2048 (он же сдвиг вправо на 11 разрядов). А если умножать не на 204.8, а на 205 - результат для целых чисел будет эквивалентным, по крайней мере в диапазоне 0..255 (
P.S. а на самом деле - даже в диапазоне 0..1028).
Для чисел большей разрядности можно тоже подобрать (и компиляторы это часто умеют) такие числа, которые будут давать правильный результат.
P.S.
Кстати, попробовал ваш совет применить в вышеуказанном проекте, и... фиаско:
"До" - размер бинарника прошивки 1290 байт:
Код: Выделить всё
for (i = 0; i < DIGITS; i++) {
if (number == 0 && i > dot)
break;
ind[i] = num[number % 10];
number /= 10;
}
После - размер бинарника прошивки 1372 байта:
Код: Выделить всё
for (i = 0; i < DIGITS; i++) {
if (number == 0 && i > dot)
break;
uint16_t i10 = number / 10;
ind[i] = num[number - i10 * 10];
number = i10;
}
После "оптимизации" прошивка стала больше на 82 байта.
P.P.S.
Целочисленное деление на 10 вовсе не добавляет "ещё одно деление". Раз уж при вычислении остатка от деления число уже было один раз поделено на 10 (и промежуточный результат деления был помещён в какой-то регистр), второй раз комплиятор этого делать не будет, и num /= 10 сведётся просто к забиранию из этого регистра готового результата.