При чтении исходного кода Lua's я заметил, что Lua использует macro
для округления double
до 32-разрядного int
. Я извлек macro
, и он выглядит так:
union i_cast {double d; int i[2]};
#define double2int(i, d, t) \
{volatile union i_cast u; u.d = (d) + 6755399441055744.0; \
(i) = (t)u.i[ENDIANLOC];}
Здесь ENDIANLOC
определяется как endianness, 0
для маленького endian, 1
для большого endian. Lua тщательно обрабатывает сущность. t
обозначает целочисленный тип, например int
или unsigned int
.
Я провел небольшое исследование и там был более простой формат macro
, который использует ту же мысль:
#define double2int(i, d) \
{double t = ((d) + 6755399441055744.0); i = *((int *)(&t));}
Или в стиле С++:
inline int double2int(double d)
{
d += 6755399441055744.0;
return reinterpret_cast<int&>(d);
}
Этот трюк может работать на любой машине, используя IEEE 754 (что означает почти все машины сегодня). Он работает как для положительных, так и для отрицательных чисел, а округление следует за Правилом банкира. (Это не удивительно, так как это следует за IEEE 754.)
Я написал небольшую программу для тестирования:
int main()
{
double d = -12345678.9;
int i;
double2int(i, d)
printf("%d\n", i);
return 0;
}
И он выводит -12345679, как и ожидалось.
Я хотел бы подробно рассказать, как работает этот сложный macro
. Магическое число 6755399441055744.0
на самом деле 2^51 + 2^52
или 1.5 * 2^52
, а 1.5
в двоичном формате может быть представлено как 1.1
. Когда к этому магическому числу добавляется любое 32-битное целое число, ну, я проиграл здесь. Как этот трюк работает?
P.S: Это в исходном коде Lua, Llimits.h.
UPDATE
- Как отмечает @Mysticial, этот метод не ограничивается 32-разрядным
int
, он также может быть расширен до 64-битногоint
, пока номер находится в диапазон 2 ^ 52. (macro
нуждается в некоторой модификации.) - Некоторые материалы говорят, что этот метод нельзя использовать в Direct3D.
-
При работе с ассемблером Microsoft для x86 существует четное быстрее
macro
, записанный вassembly
(это также извлекается из источника Lua):#define double2int(i,n) __asm {__asm fld n __asm fistp i}
-
Аналогичное магическое число для числа одиночной точности:
1.5 * 2 ^23