Рассмотрим следующий код, который является наивным способом генерации целочисленного идентификатора на основе текущего времени, где Result
равно Int64:
dtRef := Now;
Result := YearOf(dtRef) * 100000000000 +
MonthOf(dtRef) * 1000000000 +
DayOf(dtRef) * 10000000 +
HourOf(dtRef) * 100000 +
MinuteOf(dtRef) * 1000 +
SecondOf(dtRef) * 10 +
m_nLastTaskID;
Например, сгенерированный сегодня идентификатор дает 20190503163412142 (<2 ^ 55), что находится в пределах диапазона Int64 (2 ^ 63 - 1).</p>
Однако это дает целочисленное переполнение как в Берлине, так и в Рио.Он компилируется в:
MyUnitU.pas.580: Result := YearOf(dtRef) * 100000000000 +
007BCEAE 6A17 push $17
007BCEB0 6800E87648 push $4876e800
007BCEB5 FF75EC push dword ptr [ebp-$14]
007BCEB8 FF75E8 push dword ptr [ebp-$18]
007BCEBB E84CF2D3FF call YearOf
007BCEC0 0FB7C0 movzx eax,ax
007BCEC3 33D2 xor edx,edx
007BCEC5 E8FA0AC5FF call @_llmulo
007BCECA 7105 jno $007bced1
007BCECC E83FC7C4FF call @IntOver
007BCED1 52 push edx
007BCED2 50 push eax
007BCED3 FF75EC push dword ptr [ebp-$14]
007BCED6 FF75E8 push dword ptr [ebp-$18]
007BCED9 E852F2D3FF call MonthOf
007BCEDE 0FB7C0 movzx eax,ax
007BCEE1 BA00CA9A3B mov edx,$3b9aca00
007BCEE6 F7E2 mul edx
007BCEE8 7105 jno $007bceef
007BCEEA E821C7C4FF call @IntOver
В первом умножении используется _llmulo
(64-разрядное умножение со знаком, с проверкой переполнения), в то время как во втором используется обычный mul
.Ниже приведено второе умножение, которое я прокомментировал:
007BCED9 E852F2D3FF call MonthOf // Put the month (word) on AX
007BCEDE 0FB7C0 movzx eax,ax // EAX <- AX as a double word
007BCEE1 BA00CA9A3B mov edx,$3b9aca00 // EDX <- 1000000000
007BCEE6 F7E2 mul edx // EDX:EAX <- EAX * EDX
007BCEE8 7105 jno $007bceef // Jump if overflow flag not set
007BCEEA E821C7C4FF call @IntOver // Called (overflow flag set!)
Я думал, что флаг переполнения установлен на _llmulo
из-за этого сообщения об ошибке о проблемах с _llmulo
(есть такжекомментарий к источнику о том, что проверка переполнения не работает).
Однако при отладке флаг переполнения фактически устанавливается после mul
!Согласно Руководству Intel :
Флаги OF и CF устанавливаются в 0, если верхняя половина результата равна 0;в противном случае они устанавливаются на 1.
В этом случае EDX
равно 0x2A05F200
, а EAX
равно 0x00000001
после mul
, поэтому кажется, что флаг OF должен быть установленв самом деле.Вопрос в том, правильна ли здесь mul
?Это проблема с компилятором или проблема с моим кодом?
Я заметил, что если я приписываю результат MonthOf
и т. Д. Переменным Int64 до умножения на 1000 ..., все умножения выполняются с использованием _llmulo
и работает нормально.