#delphi
#delphi
Вопрос:
Result:= b8;
Result:= Result (b7 SHL 8);
Result:= Result (b6 SHL 16);
Result:= Result (b5 SHL 24);
Result:= Result (b4 SHL 32);
Result:= Result (b3 SHL 40); <------ here
Result:= Result (b2 SHL 48);
Result:= Result (MSB SHL 56);
(Результат равен int64, а b1-b8 — байт).
Но компилятор жалуется на «постоянное значение exp нарушает границы поддиапазона». Затем я обнаружил это предупреждение «Компилятор отклонит жестко заданные значения сдвига вправо, которые превышают 32, если только тип данных не Int64. То же самое не верно для Shl » на веб-сайте. В справке Delphi ничего не говорится об этом. Почему существует ограничение? Могу ли я остановить это? Может быть, директива компилятора или что-то в этомроде? Я могу использовать умножение, чтобы преодолеть это, но это будет медленнее. Есть более быстрый способ сделать это?
Редактировать: Я думаю также о создании двух кардиналов, потому что я могу использовать SHL для кардиналов, а затем объединить кардиналов вместе. Для этого потребуется только одно умножение. Есть еще идеи?
Правка2: Код является частью алгоритма, который преобразует число из базового значения 255 в базовое значение 256 (и наоборот). Я делаю это 50000 миллионов раз; поэтому важна скорость.
Комментарии:
1. Результат имеет тип Int64? И b3 тоже? Можете ли вы вставить приведение типов в Int64: Int64 (b3)?
2. Извините. Извините. Я совершенно забыл об этом. Я обновил свой вопрос.
Ответ №1:
Я бы избегал всей арифметики (в вашем коде есть дополнения и сдвиги) и делал это следующим образом:
Int64Rec(Result).Bytes[0] := b8;
Int64Rec(Result).Bytes[1] := b7;
//etc.
Int64Rec
определяется в SysUtils следующим образом:
Int64Rec = packed record
case Integer of
0: (Lo, Hi: Cardinal);
1: (Cardinals: array [0..1] of Cardinal);
2: (Words: array [0..3] of Word);
3: (Bytes: array [0..7] of Byte);
end;
Если бы вы сохранили свои байты в массиве, то вы могли бы обернуть все это в for
цикл.
Комментарии:
1. @Altar Неважно. Я не верю в идею, что вы должны отвечать на заданный вопрос. По моему опыту разработки, самое сложное — определить, какой вопрос правильный. Поэтому я всегда стараюсь рассмотреть альтернативные подходы к любой проблеме. Вот как я это делаю, когда выполняю свою повседневную работу, и я не вижу причин менять тактику на SO.
2. @Altar Нет необходимости создавать новый вопрос, и я принял ваши рассуждения о принятии другого ответа. Вы меня не обидели. В основном мне грустно, что неэлегантный код получает галочку рядом с ним, что может побудить других разработчиков продолжать использовать сдвиги и магические константы для решения таких проблем. Принятый ответ был бы намного лучше, если бы в нем действительно были некоторые подробности относительно того, почему компилятор ведет себя так, как он делает (и он делает это по очень веским причинам) и объяснил, что существуют более элегантные способы достижения этой конкретной цели.
3. 1 Для элегантности shl имеет свои применения, но это не одно из них, ваш код намного лучше.
4. Отличная идея.. легко читается, и это должно быть быстро.. Если ваш код должен быть переносимым, я полагаю, вам нужно учитывать здесь порядковый номер, верно?
5. @wouter да, это правда. Я никогда не кодировал для big endian machine. У меня нет опыта использования общих методов для обработки этого.
Ответ №2:
Я предполагаю, что ваш Result
уже Int64
, и b8
, b7
и так далее объявлены как Byte
. Компилятору нужна небольшая помощь. Приведение к Int64
:
Result := b8;
Result := Result (b7 shl 8);
Result := Result (b6 shl 16);
Result := Result (b5 shl 24);
Result := Result (Int64(b4) shl 32);
Result := Result (Int64(b3) shl 40);
Result := Result (Int64(b2) shl 48);
Result := Result (Int64(msb) shl 56);
Комментарии:
1. У меня все еще сильная аллергическая реакция на это решение. Я согласен, что это может сработать, но зачем использовать всю эту арифметику, сдвиг битов, магические константы? Конечно, лучше назначить каждый байт напрямую? Я не могу одобрить подобный код, когда существуют элегантные опции.
2. Да, но вопрос был конкретно о shl.
3. @David : Это абсолютно правильный ответ на поставленный вопрос. Я бы согласился, что, возможно, следовало задать другой вопрос, но, возможно, фрагмент кода был просто неудачным выбором, чтобы показать проблему SHL?
4. @TOndrej Но OP даже выразил обеспокоенность по поводу производительности. Трудно представить, что копирование в память арифметика int64 может быть быстрее, чем простое копирование в память.
5. Беспокойство по поводу производительности является спорным, поскольку мы не знаем контекста или даже предполагаемой цели. Я просто ответил на конкретный вопрос о shl, поскольку мне не хотелось сегодня играть в Columbo. 😉
Ответ №3:
Вот другое решение, которое использует сдвиги, но я считаю, что оно «чище» (хотя и не такое чистое, как предложение Дэвида):
result := MSB;
result := (result shl 8) or b2; { could use "shl sizeof(b2)" instead of 8 }
result := (result shl 8) or b3;
etc
result := (result shl 8) or b8;
это решение позволяет избежать всех «волшебных» сдвигов значений и, вероятно, его легче понять. Кроме того, если бы MSB..b8 (или b1 ..b8) представляли собой массив байтов, приведенный выше код можно было бы легко преобразовать в однострочный цикл.
Чтобы ответить на ваш вопрос о том, почему компилятор не принимает значение 40 и выше, причина, скорее всего, связана с этой цитатой из тома 2B набора инструкций Intel для инструкции SHLD:
В не 64-разрядных режимах и 64-разрядном режиме по умолчанию; используются только биты от 0 до 4 от количества. Это маскирует количество до значения от 0 до 31. Если количество больше размера операнда, результат не определен.
Эквивалентное условие в инструкции SHL не такое строгое:
8086 не маскирует количество сдвигов. Однако все другие процессоры IA-32 (начиная с процессора Intel 286) маскируют количество сдвигов до 5 бит, что приводит к максимальному количеству 31. Это маскирование выполняется во всех режимах работы (включая режим virtual-8086), чтобы сократить максимальное время выполнения инструкций.
В любом случае значение, большее 31, либо бесполезно (при использовании SHL), либо не определено (при использовании SHLD). Компилятор, очевидно, знает это и не позволяет вам писать код, который потенциально ошибочен (в 64-разрядном режиме).)
если вы действительно выполняете эту операцию 50 МИЛЛИАРДОВ раз, тогда вы можете рассмотреть возможность выполнения ее во встроенной сборке, что было бы довольно просто:
asm
xor eax, eax
or eax, MSB { or b1 depending on how you named the MSB }
shl eax, 8
or eax, b2
shl eax, 8
or eax, b3
shl eax, 8
or eax, b4
mov High32bit, eax
end;
и повторите описанную выше операцию для младшего 32-разрядного dword и с b5 по b8. Я не предлагал использовать SHLD, потому что я не верю, что Delphi поддерживает 64-разрядные регистры или 64-разрядные инструкции (я могу ошибаться, я никогда не пробовал это.)
Примечание: До меня дошли слухи, что 64-разрядная версия Delphi не будет поддерживать встроенную сборку. Это может быть или не быть так, но я бы держался подальше от встроенной сборки, если это действительно не является абсолютно необходимым.
Надеюсь, это поможет,
Джон.
PS: есть еще одна причина, по которой решение Дэвида Хеффернана является лучшим. В представленном мной решении каждая инструкция зависит от предыдущей инструкции (то есть eax должно быть сдвинуто на 8, прежде чем может быть выполнена следующая команда «или»). Решение Дэвида устанавливает отдельные байты, таким образом, каждое назначение не зависит от предыдущего назначения, это позволит процессору потенциально выполнять несколько назначений параллельно. В эту эпоху многоядерных процессоров это потенциально может быть немного быстрее, чем ассемблерный код, который я привел в качестве примера.
Ответ №4:
Вот функция для преобразования целого числа в int64bit: пример: AData = 36 —> Будет установлен бит 36 и т.д.
uses SysUtils;
[...]
function IntegertoInt64Bit(AData : Integer): UInt64;
var
idx : integer;
begin
result := 0;
for idx := 7 downto 0 do
begin
if(AData >= idx * 8) then
begin
Int64Rec(result).Bytes[idx] := 1 shl ((AData-1) mod 8);
break;
end;
end;
end;