Расчет размера файла, Int64 и различия между 32-битным и 64-битным - PullRequest
3 голосов
/ 10 февраля 2011

У меня были проблемы со следующим кодом:

var
  FileSize : Int64;
...
FileSize := Info.nFileSizeLow or (Info.nFileSizeHigh shl 32);

Я ожидал, что он будет работать из-за типа Int64 в левой части назначения.Но это не так.Частичное вычисление, содержащее shl, похоже, приводит к переполнению.

Поэтому я изменил его на:

FileSize := Info.nFileSizeLow or (Int64 (Info.nFileSizeHigh) shl 32);

, который работает в моей 32-битной операционной системе, но не работает в Vista64 бит!

Наконец,

FileSize := Info.nFileSizeHigh;
FileSize := FileSize shl 32;
FileSize := Info.nFileSizeLow or FileSize;

работает на обеих системах.

Может кто-нибудь объяснить различия в этих трех версиях?

Ответы [ 5 ]

5 голосов
/ 10 февраля 2011

Вообще говоря, тип выражения a * b, где a и b имеют тип Integer, а * - это оператор, который применяется к Integer, - это целочисленный тип с тем же диапазоном, что и Integer.(Обычно я говорю, что исключение составляет /.) Чтобы оператор мог использовать 64-битные операции, один или несколько операндов должны иметь диапазон, который может быть выражен только с 64-битным типом.Это должно привести к переводу всех операндов в 64-битные и выполнению 64-битной операции.

Тот факт, что левая часть назначения является 64-битным, обычно не влияет наинтерпретация и набор выражения в правой части оператора присваивания.Так обстоит дело почти со всеми известными мне языками, в которых статически отправляются 32-битные и 64-битные операторные перегрузки (в отличие от полиморфно-диспетчеризированных операторов на целых числах произвольной точности или числовых вышках и т. Д.);заставить себя вести себя иначе, было бы очень удивительным поведением.

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

var a, b: Integer;
// ...
P((a shl 16) or b); // 32-bit operation or 64-bit operation?

Я не знаюзнать, почему вы видите другое поведение со своей второй и третьей версиями кода.Насколько я вижу, они должны интерпретироваться одинаково, и в моих тестах они интерпретируются одинаково.Если бы вы могли предоставить пример кода, который работает в 32-битной Windows, но не работает в 64-битной Windows, я мог бы продолжить исследование.

2 голосов
/ 10 февраля 2011

На самом деле, это довольно хорошо задокументировано в файле справки Delphi 7, в разделе «Целочисленные типы»:

Как правило, арифметические операции над целыми числами возвращают значение типа Integer, которое в его текущей реализации эквивалентно 32-разрядному Longint. Операции возвращают значение типа Int64 только при выполнении с одним или несколькими операндами Int64. Следовательно, следующий код дает неверные результаты.

Приведенный пример кода:

var
  I: Integer;
  J: Int64;
  ...
I := High(Integer);
J := I + 1;

Чтобы получить возвращаемое значение Int64 в этой ситуации, приведите I к Int64:

 ...
J := Int64(I) + 1;
1 голос
/ 10 февраля 2011

Прежде всего, FileSize должен быть определен как UInt64, а не Int64 ...

UInt64 (недоступно в ранних версиях Delphi) - это 64-разрядное целое число без знака, или QWORD.Это ожидаемый тип для FileSize (вы не ожидаете отрицательного размера файла, не так ли?).

ИМХО, вы могли бы кодировать - используя UInt64, потому что мы не хотим иметь некоторые значениясообщается как отрицательное:

FileSize := UInt64(Info.nFileSizeLow) or (UInt64(Info.nFileSizeHigh) shl 32));

Но в Delphi 7 он выдает точно такой же код, что и ваш.

FileSize := Info.nFileSizeLow or (Int64(Info.nFileSizeHigh) shl 32));

Так что, возможно, существует некоторая регрессия компилятора.Не могли бы вы взглянуть на сгенерированный asm-код (пошаговый отладчик, затем Alt + F2) и посмотреть, есть ли разница.Но это маловероятно ...

Во всех случаях вот лучший (и более быстрый) код:

with Int64Rec(FileSize) do
begin
  Lo := Info.nFileSizeLow;
  Hi := Info.nFileSizeHigh;
end;

Официальная документация MSDN 1017 * говорит о структуре WIN32_FIND_DATA:

nFileSizeHigh: старшее значение DWORD размера файла в байтах.

Это значение равно нулю, если размер файла не превышает MAXDWORD.

Размер файла равен (nFileSizeHigh * (MAXDWORD + 1)) + nFileSizeLow.

nFileSizeLow: младшее значение DWORD размера файла в байтах.

Вот результирующий код:

FileSize := UInt64(Info.nFileSizeLow)+(UInt64(Info.nFileSizeHigh)*UInt64(1 shl 32));

Довольно забавное определение, действительно ...

0 голосов
/ 10 февраля 2011

тест на Delphi 7 и версии 2 в порядке.Должна быть ошибка более поздней версии

0 голосов
/ 10 февраля 2011

Это не совсем ответ, но это слишком долго для комментария.

Я заметил, что Delphi запутывается, когда результат выражения записывается в 64-битную переменную, но операнды 32-битные. Я столкнулся с этой ошибкой, когда реализовывал хэш-функцию, возвращающую 64-битное число. Ваш третий вариант работает, потому что вы сначала присваиваете 64-битную переменную, помогая Delphi выяснить, что ей действительно нужно выполнять 64-битную арифметику.

Мне хочется сказать, что оба варианта (1) и (2) фактически терпят неудачу, потому что Delphi генерирует 32-битную арифметику и затем присваивает результат 64-битной переменной. Я испытываю желание сказать, что вариант, который хорошо работает на вашей 32-битной машине, выигрывает от своего рода «неудачного отказа» (то есть: код плохой, но тем не менее он дает хорошие результаты для данного теста). Проблема в том, что скомпилированный код не изменяется при перемещении с 32-битной машины на 64-битную. Если код такой же, ввод такой же, вам нужно было бы зафиксировать ошибку на процессоре, но вы знаете, что не обнаружили ошибку в вашем процессоре, поэтому вам придется отступить и переосмыслить тесты, или прикрепить его к «unluck non-fail».

...