C ++ Float Division and Precision - PullRequest
10 голосов
/ 14 мая 2011

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

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

Ответы [ 5 ]

22 голосов
/ 14 мая 2011

Ну, вот одна проблема.Значение 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 бит точности.

См. Что должен знать каждый программист о плавающемТочечная арифметика .

5 голосов
/ 14 мая 2011

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

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

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

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

1 голос
/ 17 мая 2011

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

Бьюсь об заклад, кто-то будет -1 меня за комментарии и не отвечает.

_____ Редактировать _____

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

По предмету 511/512 вы можете начать со значения 1.0. В плавающей точке это может быть выражено как i.000000 ... * 2 ^ 0 или неявный набор битов (в 1), умноженный на 2 ^ 0, то есть равный 1. Так как 511/512 меньше 1, вам нужно начать со следующего меньшая мощность -1, что дает i.000000 ... * 2 ^ -1, т.е. 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. Из i.11111111 мы находим, что это число представляет 9 двоичных разрядов точности и, соответственно, 9 десятичных разрядов.

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

Рассмотрим i.11111111111111111111111 (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/8). 6 двоичных цифр точности и 4 десятичных знака.

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

1 голос
/ 14 мая 2011

Это «округленное» значение больше похоже на то, что отображается через какой-либо метод вывода, а не на то, что фактически сохраняется. Проверьте фактическое значение в вашем отладчике.

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

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

0 голосов
/ 14 мая 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...