Давайте сделаем один шаг за раз. Во-первых:
double d = value ^ (value - !!value);
Если value = 0
, то это означает 0 ^ (0 - 0), поэтому d
равно 0.
Если value != 0
, то это оценивается как значение ^ (значение - 1). Это приводит к установке младшего бита и младших нулевых битов в единицу, а всех остальных битов в ноль. например:
value = 010100100
d = 000000111
Это происходит потому, что (value - 1)
совпадает с value
, за исключением того, что младшая строка нулевых битов становится единицей, а следующий бит становится нулем из-за переноса
value = 010100100
value - 1 = 010100011
XOR value = 000000111
В любом случае, d
загружается со значением с плавающей запятой этого значения. Следующая строка:
return (((int*)&d)[1]>>20)-1023;
Это извлекает показатель с плавающей запятой и добавляет обратно смещение. Обратите внимание, что это предполагает систему с прямым порядком байтов, такую как x86; в системе с прямым порядком байтов вам нужно использовать [0]
. Он также делает предположения о размере int
с и doubles
- в частности, он предполагает 32-разрядные целочисленные значения и 64-разрядные значения IEEE для двойных чисел.
Ключевым моментом здесь является то, что неденормированные значения IEEE с плавающей запятой (и 32-битное целое число в двоичном всегда будет неденормированным) в конечном итоге будут выглядеть немного как 1.xxxxxxxx * 2^(e-1023)
, где xxxxxxxx
дробная составляющая, а e
- показатель степени. Поскольку вы расположили интересующие вас биты как биты высшего порядка, экспонента соответствует искомому значению.
Тем не менее, вы, вероятно, не сможете использовать это на ПЛК - хотя это довольно умный взлом, он эффективен даже удаленно, если у вас есть аппаратный FPU; и даже в системах x86 встроенные целочисленные операции быстрее . В этом вопросе есть ряд других техник ; Вы, вероятно, сможете найти более быстрый там. Ваш ПЛК также может иметь встроенную операцию для выполнения этой операции в одной инструкции.