Это правда, что IEEE с плавающей точкой имеет только ограниченное количество битов мантиссы. Если имеется 23 бита мантиссы, то он может точно представлять 2 23 различных целочисленных значений.
Но поскольку с плавающей точкой хранит показатель степени два отдельно, он может (при условии ограниченного диапазона показателей) представлять ровно любое из этих 2 23 значений , умноженное на степень двух .
33554432
в точности равно 2 25 , поэтому для его точного представления требуется всего один бит мантиссы (плюс двоичный показатель степени, который обозначает умножение на степень два). Его двоичное представление - 10000000000000000000000000
, которое имеет 26 битов, но только 1 значащий бит. (Ну, на самом деле они все значимы, но вы поняли.)
Вы обнаружите, что соседние целочисленные значения 33554431
и 33554433
не могут быть точно представлены в 32-битном float
. (Но они могут быть представлены в 64-битном double
.)
В более общем смысле, разница между последовательными представимыми значениями типа float
зависит от величины значения. В моей системе (большинство систем используют формат IEEE, но стандарт этого не требует) эта программа:
#include <iostream>
#include <iomanip>
#include <cmath>
void show(float f) {
std::cout << std::nextafterf(f, 0.0) << "\n"
<< f << "\n"
<< std::nextafterf(f, f*2) << "\n";
putchar('\n');
}
int main(void) {
std::cout << std::setprecision(24);
show(1);
show(1<<23);
show(1<<24);
show(1<<30);
}
производит этот вывод:
0.999999940395355224609375
1
1.00000011920928955078125
8388607.5
8388608
8388609
16777215
16777216
16777218
1073741760
1073741824
1073741952
Показывает непосредственного предшественника и преемника типа float
из чисел 1, 2 23 , 2 24 и 2 30 . Как вы можете видеть, разрывы увеличиваются для больших чисел, причем разрыв увеличивается вдвое при каждой степени 2.
Вы получите аналогичные результаты, но с меньшими пробелами, с типом double
или long double
.