Книга говорит, что c Стандарт обеспечивает точность с плавающей запятой до шести значащих цифр, но это не так? - PullRequest
0 голосов
/ 01 февраля 2019

Я читаю C Primer Plus Стивена Прата, и одним из первых способов, которым он представляет поплавки, является обсуждение того, насколько они точны до определенного момента.В нем конкретно говорится: «Стандарт C предусматривает, что число с плавающей точкой должно быть способно представлять как минимум шесть значащих цифр ... Число с плавающей точкой должно точно представлять первые шесть чисел, например, 33.333333»

Это страннодля меня, потому что это звучит как поплавок с точностью до шести цифр, но это не так.1.4 хранится как 1.39999 ... и так далее.У вас все еще есть ошибки.

Так что же конкретно предоставляется?Есть ли ограничение на то, насколько точным должно быть число?

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

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

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

Ответы [ 2 ]

0 голосов
/ 01 февраля 2019

Предположим, у вас есть десятичное число с q значащие цифры:

d q -1 . д * 1 013 ** ** 1014 1015 * д -2 * +1017 * д д * * -3 тысяча двадцать-две * +1023 * ... d 0 ,

и давайте также сделаем его десятичным числом с плавающей точкой, то есть мы будем масштабировать его до степени десяти:

*** 1 033 1 034 * д * ** 1037 тысяча тридцать-шесть * д * * -1 одна тысяча тридцать восемь * * 1 039. д * ** тысяча сорок две тысячу сорок три * д * * -2 тысячу сорок-четыре * 1 045 *d д -3 ... d 0 • 10 е .

Далее мы конвертируем это число в float.Многие такие числа не могут быть точно представлены в float, поэтому мы округляем результат до ближайшего представимого значения.(Если есть связь, мы округляем, чтобы сделать младшую цифру четной.) Результатом (если мы не переполнились или не опустились) будет некоторое число с плавающей запятой x .По определению чисел с плавающей запятой (в C 2018 5.2.4.2.2 3) оно представлено некоторым количеством цифр в некоторой базе, масштабированной этой базой до степени.Предположим, что это база два, x :

b p −1 . б * +1079 ** * р тысячу восемьдесят один * +1082 * -2 * +1083 ** +1084 * B * ** тысяча восемьдесят шесть тысяча восемьдесят семь * р -3 ... B 0 • 2 p .

Далее мы конвертируем float x обратно в десятичную форму с q значащие цифры.Точно так же значение float x может быть не совсем точно представлено в виде десятичного числа с q цифрами, поэтому мы можем получить некоторое возможное новое число:

N д -1 . п * ** тысяча сто двадцать-один ** тысяча сто двадцать-дв * д одна тысячи сто двадцать три * * -2 тысяча сто двадцать четыре * * п тысяча сто двадцать-шесть * +1129 * д -3 ... * * п тысяча сто тридцать две * ** +1134 тысяча сто тридцать-три * 0 • 10 * * 1 137 м * +1138 ** * тысяча сто тридцать-девять.

Оказывается, что для любого формата float существует некоторое число q , такое, что если десятичное число, с которого мы начинали, ограничено q цифры, то результат этого преобразования туда и обратно будет равен исходному числу.Каждая десятичная цифра q цифр, округленная до float, а затем обратно до q десятичных цифр, приводит к начальному числу.

В стандарте 2018 C, пункт 5.2.4.2.2, пункт 12, говорит нам, что это число q должно быть не менее 6 (реализация C может поддерживать большие значения), и реализация C должна определить для него символ препроцессора (вfloat.h) называется FLT_DIG.

Итак, учитывая номер вашего примера 1.4, когда мы преобразуем его в float в базовом 32-разрядном двоичном формате IEEE-754, мы получим ровно 1,39999997615814208984375 (то естьего математическое значение, показанное в десятичном виде для удобства; фактические биты в объекте представляли его в двоичном виде).Когда мы конвертируем это в десятичную с полной точностью, мы получаем «1.39999997615814208984375».Но если мы преобразуем его в десятичное с округлением до шести цифр, мы получим «1.40000».Таким образом, 1.4 выживает в обоих направлениях.

Другими словами, неверно в целом, что шесть десятичных цифр могут быть представлены в float без изменений, но верно, что float несет достаточно информации , чтобы вы могли восстановить шесть десятичных цифр из нее.

Конечно, как только вы начнете делать арифметику, ошибки, как правило, составят, и вы не сможетебольше полагаться на шесть десятичных цифр.

0 голосов
/ 01 февраля 2019

Спасибо Говинду Пармару за цитирование онлайн-примера C11 (или, в данном случае, C99).

"6", на которую вы ссылаетесь, это "FLT_DECIMAL_DIG".

http://c0x.coding -guidelines.com / 5.2.4.2.2.html

количество десятичных цифр n, такое что любое число с плавающей запятой с p radix b цифрамиможет быть округлено до числа с плавающей точкой с n десятичными цифрами и обратно без изменения значения,

  { p log10 b        if b is a power of 10
  {
  { [^1 + p log10 b^] otherwise

FLT_DECIMAL_DIG 6
DBL_DECIMAL_DIG 10 LDBL_DECIMAL_DIG 10

"«Ненормальный» означает:

Что такое субнормальное число с плавающей запятой?

Число является ненормальным, когда биты экспоненты равны нулю, а мантисса не равна нулю,Это числа от нуля до наименьшего нормального числа.У них нет неявного начального 1 в мантиссе.


СИЛЬНОЕ ПРЕДЛОЖЕНИЕ:

Если вы не знакомы с "арифметикой с плавающей запятой" (или, честно говоря,даже если это так), это отличная статья для чтения (или обзора):

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

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