По следам вопроса о битовом представлении вещественных чисел и моего ответа на него. Хочу программно для любого вещественного типа определить, сколько бит в нём отводится под мантиссу, а сколько под экспоненту. Для этого написал следующий код (в нём бит под знак считается отдельно от мантиссы, поэтому числа на 1 меньше): https://ideone.com/YuIWNc - код на Си (float, double, long double) https://ideone.com/342B4S - код на Си++ (float, double, long double) https://ideone.com/VURQnw - код на Си++ (float, double, long double, __float128) #include template void count(unsigned *result_m, unsigned *result_e) { typed x = 1, exp unsigned res, e for (res=0 x!=0 ++res) x/=2 for (exp=1,e=0 exp*2int main(void) { unsigned f_m, f_e, d_m, d_e, ld_m, ld_e, f128_m, f128_e count(&f_m, &f_e) count(&d_m, &d_e) count(&ld_m, &ld_e) count<__float128>(&f128_m, &f128_e) printf(" S M E SZ ") printf("float: 1 %3u %2u %3u ", f_m, f_e, 8 * sizeof(float)) printf("double: 1 %3u %2u %3u ", d_m, d_e, 8 * sizeof(double)) printf("long double: 1 %3u %2u %3u ", ld_m, ld_e, 8 * sizeof(long double)) printf("__float128: 1 %3u %2u %3u ", f128_m, f128_e, 8 * sizeof(__float128)) } Получается так: S M E SZ float: 1 23 8 32 double: 1 52 11 64 long double: 1 63 15 128 __float128: 1 112 15 128 Для float, double и даже __float128 всё работает (Википедия, IEEE 754-2008). А вот с long double возникают проблемы: 1+63+15 = 79 - 79 бит. Вместо 80. Где ещё один бит? long double представляет собой 10-байтные числа, но sizeof вернул 16. Как-то можно получить 10?
Ответ Один бит потерялся из-за того, что на платформе x86 80-битное плавающее значение имеет одно принципиальное отличие в представлении от 32- и 64-битных IEEE754 плавающих значений (float и double). float и double используют представление с неявной ведущей единицей в мантиссе. То есть в нормализованном представлении старшая единица в мантиссе не хранится явно, а лишь подразумевается. А вот в расширенном 80-битном плавающем типе long double эта ведущая единица в мантиссе всегда хранится явно. Из-за этого и возникает разница. Для типов float и double ваш первый цикл сначала итерирует через нормализованные представления числа, в которых явная мантисса всегда равна нулю, а экспонента уменьшается от половины своего максимального значения (127 для float) до значения 1: // Для `float` // Нормализованные представления: мантисса равна 0, а экспонента убывает от 127 до 1 0x3F800000 ... 0x00800000 <- после 126 делений <br>После этого ваш цикл продолжает итерировать через денормализованные представления числа, в которых экспонента равна 0, а по мантиссе вправо движется одинокая единица. Когда эта одинокая единица вылетит за правый край мантиссы, x станет равен нулю и цикл завершится // Денормализованные представления: экспонента равна 0, а мантисса состоит // из движущейся вправо единицы 0x00400000 0x00200000 ... 0x00000001 0x00000000 <- после 150 делений <br>Заметьте, в float и double единица в мантиссе возникает только в самом первом денормализованном значении и проходит через все биты мантиссы. Получается, что количество денормализованных ненулевых значений в этом случае равно количеству битов в мантиссе. Однако при использовании long double единица в старшем бите мантиссы явно присутствовала всегда, с самого начала. Когда в вашем цикле экспонента long double достигает нуля и цикл начинает подсчитывать денормализованные значения long double, единица в мантиссе не возникает "из никуда" в старшую позицию мантиссы (как это было в в float и double), а уже присутствует в старшей позиции изначально и "стартует" именно оттуда. Из-за этого та часть цикла, которая считает денормализованные значения, делает на одну итерацию меньше.
Кстати, странная манера складывать в одну сумму - res - половину диапазона экспоненты и ширину мантиссы чревата проблемами. Вы потом вычисляете значение log2 res и ожидаете, что эта величина будет правильно описывать количество битов в экспоненте. Однако если в некотором гипотетическом плавающем типе мантисса окажется очень широкой, то величина log2 res может оказаться ошибочной.