C++ Float разделение и точность

Я знаю, что 511, разделенный на 512, фактически равен 0.998046875. Я также знаю, что точность поплавков составляет 7 цифр. Мой вопрос в том, когда я делаю эту математику в C++ (GCC), результат, который я получаю, составляет 0.998047, что является округленным значением. Я бы предпочел просто получить усеченное значение 0.998046, как я могу это сделать?

  float a = 511.0f;
  float b = 512.0f;
  float c = a / b;

5 ответов


Ну, вот одна проблема. Значение 511/512, как float, Это точно. Округление не выполняется. Вы можете проверить это, запросив более семи цифр:

#include <stdio.h>
int main(int argc, char *argv[])
{
    float x = 511.0f, y = 512.0f;
    printf("%.15f\n", x/y);
    return 0;
}

выход:

0.998046875000000

A float хранится не как десятичное число, а двоичное. Если вы разделите число на степень 2, например 512, результат почти всегда будет точным. Что происходит с точностью float - это не просто 7 цифр, это действительно 23 bits of точность.

посмотреть Что Каждый Компьютерщик Должен Знать Об Арифметике С Плавающей Запятой.


Я также знаю, что точность плавает-7 цифр.

нет. Наиболее распространенный формат с плавающей запятой двоичная и имеет точность 24 бита. Это где-то между 6 и 7 десятичными цифрами, но вы не можете думать в десятичном, если хотите понять, как работает округление.

поскольку b-это степень 2, c точно представима. Именно во время преобразования в десятичное представление будет происходить округление. Стандартные способы получения десятичного знака представление не дает возможности использовать усечение вместо округления. Один из способов-попросить еще одну цифру и проигнорировать ее.

но обратите внимание, что тот факт, что c точно представим, является свойством его значения. Некоторые, по-видимому, более простые значения (например, 0.1) не имеют точного представления в двоичных форматах FP.


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

с помощью iostream и stdio вы можете указать точность вывода. Если вы укажете 7 значащих цифр, преобразуйте их в строку, а затем усеките строку перед отображением, вы получите вывод без округления.

Не могу придумать одну причину, почему вы хотели бы сделать это, однако, и учитывая subseqent объяснение приложения teh, вам было бы лучше использовать двойную точность, хотя это, скорее всего, просто проблемы шобе в другом месте.


ваш вопрос не уникален, на него уже много раз отвечали. Это не простая тема, и только потому, что ответы публикуются, не обязательно означает, что они будут хорошего качества. Если вы посмотрите немного, вы найдете действительно хорошие вещи. И это займет меньше времени.

держу пари, кто-то будет -1 меня за комментарии и не отвечает.

_____ Edit ______

что является фундаментальным для понимания плавающей точки, это реализовать что все отображается двоичными цифрами. Поскольку большинству людей Трудно понять это, они пытаются увидеть это с точки зрения десятичных цифр.

по теме 511/512 вы можете начать с просмотра значения 1.0. В плавающей точке это может быть выражено как i.000000... * 2^0 или неявный набор битов (до 1), умноженный на 2^0, т. е. равен 1. С 511/512 меньше 1 вам нужно начать с более низкую мощность -1 дает мне.000000... * 2^-1 i e 0.5. Обратите внимание, что только что изменилось, так это показатель. Если мы хотим выразить 511 в двоичном формате, мы получаем 9 единиц-111111111 или с плавающей запятой с неявным битом i.11111111-который мы можем разделить на 512 и сложить с показателем -1, дающим i.1111111100... * 2^-1.

Как это переводится на 0.998046875?

Ну для начала неявный бит представляет 0.5 (или 2^-1), первый явный бит 0.25 (2^-2), следующий явный бит 0.125 (2^-3), 0.0625, 0.03125 и так до тех пор, пока вы представили девятый бит (восьмой явный). Суммируйте их, и вы получите 0.998046875. От меня.11111111 мы находим, что это число составляет 9 двоичных цифр точности и, кстати, 9 точных десятичных.

Если вы умножите 511/512 на 512, вы получите i1111111100... * 2^8. Здесь есть те же девять двоичных цифр точности, но только три десятичные цифры (для 511).

рассмотрим i.1111111111111111111111111 (i + 23 единицы) * 2^-1. Мы получим долю (2^(24-1)^/(2^24))с 24 двоичными и 24 десятичными цифрами точности. При соответствующем форматировании printf будут отображаться все 24 десятичные цифры. Умножьте его на 2^24, и у вас все еще есть 24 двоичных цифры точности, но только 8 десятичных (для 16777215).

теперь рассмотрим i.1111100... * 2^2, который выходит на 7,875. i11-целочисленная часть и 111-дробная часть (111/1000 или 7/8ths). 6 двоичных цифр точности и 4 десятичных.

думая десятичное при выполнении плавающая точка крайне вредна для понимания. Освободите себя!


Если вас просто интересует значение, вы можете использовать double, а затем умножить результат на 10^6 и выровнять его. Снова делим на 10^6, и вы получите усеченное значение.