Мне надо перевести фрагмент кода с C++ на Delphi. В частности эту функцию:
static int sizeofints( const int num_of_ints, unsigned int sizes[]) {
int i, num;
unsigned int num_of_bytes, num_of_bits, bytes[32], bytecnt, tmp;
num_of_bytes = 1;
bytes[0] = 1;
num_of_bits = 0;
for (i=0; i < num_of_ints; i++) {
tmp = 0;
for (bytecnt = 0; bytecnt < num_of_bytes; bytecnt++) {
tmp = bytes[bytecnt] * sizes[i] + tmp;
bytes[bytecnt] = tmp & 0xff;
tmp >>= 8;
}
while (tmp != 0) {
bytes[bytecnt++] = tmp & 0xff;
tmp >>= 8;
}
num_of_bytes = bytecnt;
}
num = 1;
num_of_bytes--;
while (bytes[num_of_bytes] >= num) {
num_of_bits++;
num *= 2;
}
return num_of_bits + num_of_bytes * 8;
}
Тут какое-то вычисление, сколько бит понадобится на хранение нескольких чисел указанного максимального размера. И меня смутило, что переменная bytecnt, которая вначале служит счётчиком цикла, используется также после цикла. Это нормально в C++?
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Здравствуйте, Khimik, Вы писали:
K>Мне надо перевести фрагмент кода с C++ на Delphi. В частности эту функцию:
K>Тут какое-то вычисление, сколько бит понадобится на хранение нескольких чисел указанного максимального размера. И меня смутило, что переменная bytecnt, которая вначале служит счётчиком цикла, используется также после цикла. Это нормально в C++?
Да. Используй цикл while, потому что for в С++ и Дельфи не всегда невзаимозаемы.
Одно в другое можно перевести только в простом случает когда переменная цикла объявляется прямо в заголовке цикла, имеет соавнение с числом в качестве верхнего условия и инкремент/декремент на единичку в качестве перехода
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, Khimik, Вы писали:
K>И меня смутило, что переменная bytecnt, которая вначале служит счётчиком цикла, используется также после цикла. Это нормально в C++?
Можешь вместо такого.
for (bytecnt = 0; bytecnt < num_of_bytes; bytecnt++) {
Написать вот так.
for (int bytecnt = 0; bytecnt < num_of_bytes; bytecnt++) {
И даже вот так.
for (int bytecnt = 0; bytecnt < num_of_bytes; ++bytecnt) {
Вот такой стиль,
int i, num;
unsigned int num_of_bytes, num_of_bits, bytes[32], bytecnt, tmp;
когда предварительно объявляешь локальные переменные, да ещё и скопом, пришёл из древности. Вспоминается какой-нибудь Pascal.
Скажем так, если ты говоришь об условно хорошем стиле написания, то это не очень хорошо. Но с другой стороны всё это допустимо и будет прекрасно работать.
А если захочешь изменить стиль как я выше написал, то придётся ниже переписать алгоритм, потому что bytecnt не будет в той области видимости.
Здравствуйте, T4r4sB, Вы писали:
TB>Да. Используй цикл while, потому что for в С++ и Дельфи не всегда невзаимозаемы. TB>Одно в другое можно перевести только в простом случает когда переменная цикла объявляется прямо в заголовке цикла, имеет соавнение с числом в качестве верхнего условия и инкремент/декремент на единичку в качестве перехода
Ок, вот этот перевод корректный?
function cbsizeofints(num_of_ints: Integer; sizes: array of int64): Integer;
var
i, num: Integer;
num_of_bytes, num_of_bits, bytecnt, tmp: int64;
bytes: array[0..31] of int64;
begin
num_of_bytes := 1;
bytes[0] := 1;
num_of_bits := 0;
for i := 0 to num_of_ints - 1 do
begin
tmp := 0;
bytecnt:=0;
while bytecnt<num_of_bytes - 1 do
begin
tmp := bytes[bytecnt] * sizes[i] + tmp;
bytes[bytecnt] := tmp and $ff;
tmp := tmp shr 8;
inc(bytecnt);
end;
while tmp <> 0 do
begin
bytes[bytecnt] := tmp and $ff;
tmp := tmp shr 8;
Inc(bytecnt);
end;
num_of_bytes := bytecnt;
end;
num := 1;
Dec(num_of_bytes);
while bytes[num_of_bytes] >= num do
begin
Inc(num_of_bits);
num := num * 2;
end;
Result := num_of_bits + num_of_bytes * 8;
end;
Тут мне не совсем понятно, если превращать for в while, инкремент надо ставить в начале теле цикла, или в конце.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Здравствуйте, Khimik, Вы писали:
K>Ок, вот этот перевод
Unsigned тебе ни о чем не говорит? Даже банально на русский переведи хотя бв
K>Тут мне не совсем понятно, если превращать for в while, инкремент надо ставить в начале теле цикла, или в конце.
В конце
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, Khimik, Вы писали:
K>Тут какое-то вычисление, сколько бит понадобится на хранение нескольких чисел указанного максимального размера. И меня смутило, что переменная bytecnt, которая вначале служит счётчиком цикла, используется также после цикла. Это нормально в C++?
Да. Она описана не в for, а выше, поэтому может использоваться до выхода на свою }
Здравствуйте, T4r4sB, Вы писали:
K>>Ок, вот этот перевод
TB>Unsigned тебе ни о чем не говорит? Даже банально на русский переведи хотя бв
Ну я перевёл unsigned int как int64, формально на Delphi надо как Cardinal (32-битный интегер без знака). Это же не принципиально, int64 для всего годится.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
K>static int sizeofints( const int num_of_ints, unsigned int sizes[]) {
K> int i, num;
K> unsigned int num_of_bytes, num_of_bits, bytes[32], bytecnt, tmp;
K> num_of_bytes = 1;
K> bytes[0] = 1;
K> num_of_bits = 0;
K> for (i=0; i < num_of_ints; i++) {
K> tmp = 0;
K> for (bytecnt = 0; bytecnt < num_of_bytes; bytecnt++) {
K> tmp = bytes[bytecnt] * sizes[i] + tmp;
K> bytes[bytecnt] = tmp & 0xff;
K> tmp >>= 8;
K> }
K> while (tmp != 0) {
K> bytes[bytecnt++] = tmp & 0xff;
K> tmp >>= 8;
K> }
K> num_of_bytes = bytecnt;
K> }
K> num = 1;
K> num_of_bytes--;
K> while (bytes[num_of_bytes] >= num) {
K> num_of_bits++;
K> num *= 2;
K> }
K> return num_of_bits + num_of_bytes * 8;
K>}
K>
K>Это нормально в C++?
Нет. Код больше поход на Си. Более того, похоже на какую-то числодробилку (условную в данном случае) с заточкой на минимальное использование памяти и перформанс. Такой код обычно редко правится и упор идет не на читабельность. Вот там такое принято, да
PS Хотя пустые строки никто не запрещает вставлять для визуального разделения на блоки
Здравствуйте, Khimik, Вы писали:
K>Тут мне не совсем понятно, если превращать for в while, инкремент надо ставить в начале теле цикла, или в конце.
В конце.
int num_of_bytes = 2, bytecnt = 0;
while (bytecnt < num_of_bytes)
{
// код в цикле
bytecnt++;
}
int num_of_bytes = 2;
for (int bytecnt = 0; bytecnt < num_of_bytes; bytecnt++)
{
// код в цикле
}
1. Если объявляешь и определяешь переменные в инициализации цикла for, то они будут видны только внутри области видимости цикла.
2. Цикл for в C++ это не такая примитивная штука, как в Pascal. Можно сделать несколько инициализаций через запятую. Можно так же добавить несколько выражений конца цикла опять же через запятую. Можно создать счётчик цикла, а можно какое-нибудь другое вычисление.
1. Инициализация выполняется один раз можно сказать, что вне петли цикла, только область видимости будет действовать только на петлю.
2. Предусловие выполняется каждый раз в начале петли. А это считай условие while, то есть for это не просто цикл со счётчиком, а цикл с предусловием в котором можно задать счётчик, а можно задать и не счётчик.
3. Выражение, которое выполняется в конце петли.
Вот это считай одно и тоже.
int num_of_bytes = 2, bytecnt = 0;
while(bytecnt < num_of_bytes)
{
// код в цикле
bytecnt++;
}
Просто меняем на for и оставляем пустую инициализацию и выражение конца петли.
int num_of_bytes = 2, bytecnt = 0;
for (;bytecnt < num_of_bytes;)
{
// код в цикле
bytecnt++;
}
Здравствуйте, Khimik, Вы писали:
K>Здравствуйте, T4r4sB, Вы писали:
K>>>Ок, вот этот перевод
TB>>Unsigned тебе ни о чем не говорит? Даже банально на русский переведи хотя бв
K>Ну я перевёл unsigned int как int64, формально на Delphi надо как Cardinal (32-битный интегер без знака). Это же не принципиально, int64 для всего годится.
Это принципиально, у знаковых чисел другая реакция на пепеполнения, на сдвиги (?), деление другое.
А еще другой размер может тоде гдето проявиться.
В битовых операциях лучше не пренебрегать этим всем
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, T4r4sB, Вы писали:
K>>Ну я перевёл unsigned int как int64, формально на Delphi надо как Cardinal (32-битный интегер без знака). Это же не принципиально, int64 для всего годится.
TB>Это принципиально, у знаковых чисел другая реакция на пепеполнения, на сдвиги (?), деление другое. TB>А еще другой размер может тоде гдето проявиться. TB>В битовых операциях лучше не пренебрегать этим всем
По этому вопросу тоже лично я очень не люблю всякие сдвиги, мне не хочется в этом разбираться. Полагаю, почти всегда вместо например сдвига влево лучше просто умножать число в два раза (если только не актуальна скорость). Так понятнее, соответственно меньше будет ошибок.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Здравствуйте, Khimik, Вы писали:
K> По этому вопросу тоже лично я очень не люблю всякие сдвиги, мне не хочется в этом разбираться. Полагаю, почти всегда вместо например сдвига влево лучше просто умножать число в два раза (если только не актуальна скорость). Так понятнее, соответственно меньше будет ошибок.
Дело не в люблю-не-люблю и даже не в скорости , компилятор не тупой и умеет по возможности заменять умножение на сдвиг. Делл в том, какой смысл у опеоации требуется исходя из контекста. Если там важны именно битовые представления то надо писать сдвиг. Если важно числовое значение то надо умножать на 2
У тебя явно битовые манипуляции
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, Khimik, Вы писали:
K>Тут какое-то вычисление, сколько бит понадобится на хранение нескольких чисел указанного максимального размера. И меня смутило, что переменная bytecnt, которая вначале служит счётчиком цикла, используется также после цикла. Это нормально в C++?
При чем здесь C++? Последнее значение переменной, достигнутор в цикле, может быть полезным дополнительным результатом алгоритма, реализуемого этим циклом.
Другой вопрос, что именно в этой программе на выходе из цикла bytecnt сделается равным num_of_bytes, после чего его, возможно, еще разок-другой поинкрементят, а результат запихнут в num_of_bytes. Логичнее было бы там, где while (tmp != 0), num_of_bytes и использовать вместо bytecnt. Было бы чуть яснее, и одним присваиванием меньше.
Здравствуйте, T4r4sB, Вы писали:
TB>Дело не в люблю-не-люблю и даже не в скорости , компилятор не тупой и умеет по возможности заменять умножение на сдвиг. Делл в том, какой смысл у опеоации требуется исходя из контекста. Если там важны именно битовые представления то надо писать сдвиг. Если важно числовое значение то надо умножать на 2 TB>У тебя явно битовые манипуляции
Ну это понятно, в этой программе они используются для сжатия цифровых данных, а я бы ни за что не реализовывал этот алгоритм сжатия через сдвиги, мне намного комфортнее обычное умножение или div.
Кстати обратил внимание, что в C деление одинаково прописывается для целых чисел и флоатов, а нас в Delphi не так, для целочисленного деления есть div, по-моему это правильнее.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Здравствуйте, Pavel Dvorkin, Вы писали:
K>>Тут какое-то вычисление, сколько бит понадобится на хранение нескольких чисел указанного максимального размера. И меня смутило, что переменная bytecnt, которая вначале служит счётчиком цикла, используется также после цикла. Это нормально в C++?
PD>Да. Она описана не в for, а выше, поэтому может использоваться до выхода на свою }
До Студии 7.1 скоуп был от начала цикла до конца блока. Соответственно, два одинаковых цикла подрял с i написать было нельзя (второй должен был быть без объявления). Переносить проекты было незабываемое удовольствие.
Здравствуйте, Khimik, Вы писали:
K>Мне надо перевести фрагмент кода с C++ на Delphi. В частности эту функцию:
И какие трудности? Примерно так, но предполагается что sizes[i]<>0 и то что результат влазит в 32 байта. т.е. <= 256
function sizeofints(sizes:array of cardinal):integer;
var j,zn:integer;
var z:array[32] of byte;
procedure z_mul(x:cardinal);
var i:integer; carry:cardinal;
begin
i:=0; carry:=0;
while i<zn or carry<>0 do
inc(carry,z[i]*x);
z[i]:=carry; carry:=carry shr 8;
inc(i);
end
zn:=i;
end
function bits(x:byte);begin
result:=0; while x<>0 do begin inc(result); x:=x shr 1; end
end
begin
zn:=1; z[0]:=1;
for j:=0 to length(sizes)-1 do z_mul(sizes[j]);
result:=(zn-1)*8+bits(z[zn-1]);
end
K>Ну это понятно, в этой программе они используются для сжатия цифровых данных, а я бы ни за что не реализовывал этот алгоритм сжатия через сдвиги, мне намного комфортнее обычное умножение или div.
Дело не в комфорте а в закладываемом смысле. Тут битовые операции — значит пиши их
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Извиняюсь за занудство, но в моём коде на C++ в первом сообщении, когда закончится первый цикл с bytecnt и запустится while (tmp != 0) {, bytecnt будет равен num_of_bytes-1 или num_of_bytes?
Второй вопрос — строчка bytes[bytecnt++] = tmp & 0xff делает логическое умножение tmp на 256, т.е. у tmp оставляется только последний байт?
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Здравствуйте, Khimik, Вы писали:
K>Извиняюсь за занудство, но в моём коде на C++ в первом сообщении, когда закончится первый цикл с bytecnt и запустится while (tmp != 0) {, bytecnt будет равен num_of_bytes-1 или num_of_bytes?
K>Второй вопрос — строчка bytes[bytecnt++] = tmp & 0xff делает логическое умножение tmp на 256, т.е. у tmp оставляется только последний байт?
Погугли C++ playground если не хочешь себе ставить крестокомпилятор.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, Khimik, Вы писали: K>Извиняюсь за занудство, но в моём коде на C++ в первом сообщении, когда закончится первый цикл с bytecnt и запустится while (tmp != 0) {, bytecnt будет равен num_of_bytes-1 или num_of_bytes?
таким образом должно быть очевидно, что данный цикл завершится, когда условие (bytecnt < num_of_bytes) станет ложным. Т.е. после цикла bytecnt будет равен num_of_bytes. K>Второй вопрос — строчка bytes[bytecnt++] = tmp & 0xff делает логическое умножение tmp на 256, т.е. у tmp оставляется только последний байт?
Про логическое умножение на 256 не понял, такая операция мне не знакома. А так — да, верно, только правильно говорить "младший байт", а не "последний", что такое "последний байт" — вопрос совсем не очевидный.
Я попросил ChatGPT перевести код из первого сообщения, ниже напишу его вариант, за корректность не ручаюсь, но может быть будет полезно.
Скрытый текст
Here is the translated code from C++ to Delphi:
```delphi
function sizeofints(const num_of_ints: Integer; sizes: array of Cardinal): Integer;
var
i, num: Integer;
num_of_bytes, num_of_bits, bytecnt, tmp: Cardinal;
bytes: array[0..31] of Byte;
begin
num_of_bytes := 1;
bytes[0] := 1;
num_of_bits := 0;
for i := 0 to num_of_ints - 1 do
begin
tmp := 0;
for bytecnt := 0 to num_of_bytes - 1 do
begin
tmp := bytes[bytecnt] * sizes[i] + tmp;
bytes[bytecnt] := tmp and $FF;
tmp := tmp shr 8;
end;
while tmp <> 0 do
begin
bytes[bytecnt] := tmp and $FF;
Inc(bytecnt);
tmp := tmp shr 8;
end;
num_of_bytes := bytecnt;
end;
num := 1;
Dec(num_of_bytes);
while bytes[num_of_bytes] >= num do
begin
Inc(num_of_bits);
num := num * 2;
end;
Result := num_of_bits + num_of_bytes * 8;
end;
```
Explanation:
- I changed the `int` and `unsigned int` types to their Delphi equivalents: `Integer` and `Cardinal`.
- I adjusted the array parameter to be a dynamic array in Delphi.
- C++ arrays are 0-indexed as are Delphi arrays, so we didn't need to change the indexing in the translation.
- The `for` loops and `while` loops were adapted to Delphi's syntax.
- Bitwise operations remain the same, but the shift right operator is `shr` in Delphi instead of `>>`.
- The `return` statement has been changed to assigning the `Result` variable, which is how you return values from functions in Delphi.