Точное представление целых чисел в числах с плавающей запятой - PullRequest
0 голосов
/ 30 августа 2018

Я пытаюсь понять представление целых чисел в формате с плавающей запятой.

Поскольку формат IEEE с плавающей запятой имеет только 23 бита для мантиссы, я ожидаю, что любое целое число, превышающее 1 << 22, будет только приблизительным представлением. Это не то, что я наблюдаю в g ++ </p>

оба из нижеприведенных cout выводят одинаковое значение 33554432.

Поскольку часть мантиссы - это та, которая отвечает за точность, как мы можем представлять (хранить) точное число, для которого требуется более 23 бит, чтобы быть сохраненными точно.

void floating_point_precision(){
  cout<< setprecision(10);
  float fp = (1<<25);
  cout<< fp <<endl;
  cout<< (1<<25) <<endl;
}

В качестве продолжения, основанного на ответе ниже, почему следующий код не выполняет "Не равно", даже если печать обоих fp, я различаюсь.

void floating_point_precision(){
  cout<< setprecision(10);
  float fp = ((1<<25)+1);
  cout<< fp <<endl;
  int i = ((1<<25)+1)  ;
  cout<< i <<endl;
  if(i != fp)
    cout<< "Not equal" <<endl;
}

1 Ответ

0 голосов
/ 30 августа 2018

Это правда, что 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.

...