Почему тип с плавающей запятой C изменяет фактический ввод с 125.1 до 125.099998 на выходе? - PullRequest
1 голос
/ 30 июня 2011

Я написал следующую программу:

 #include<stdio.h>
    int main(void)
    {
     float f;
     printf("\nInput a floating-point no.: ");
     scanf("%f",&f);
     printf("\nOutput: %f\n",f);
     return 0;
    }

Я нахожусь на Ubuntu и использовал GCC для компиляции вышеуказанной программы.Вот мой примерный прогон и вывод, о котором я хочу узнать:

Input a floating-point no.: 125.1
Output: 125.099998

Почему меняется точность?

Ответы [ 7 ]

12 голосов
/ 30 июня 2011

Поскольку число 125.1 является , его невозможно точно представить с помощью чисел с плавающей запятой .Это происходит в большинстве языков программирования.Используйте, например, printf("%.1f", f);, если вы хотите напечатать число с одним десятичным знаком, но имейте в виду: само число не точно равно 125,1.

6 голосов
/ 01 июля 2011

Спасибо всем за ваши ответы. Хотя почти все вы помогли мне взглянуть в правильном направлении, я не мог понять точную причину такого поведения. Поэтому я провел небольшое исследование в дополнение к чтению страниц, на которые вы, ребята, указали мне. Вот мое понимание этого поведения:

Числа с плавающей запятой одинарной точности обычно используют 4 байта для хранения на архитектурах x86 / x86-64. Однако не все 32 бита (4 байта = 32 бита) используются для хранения величины числа.

Для хранения в виде плавающего типа с одинарной точностью входной поток форматируется в следующей записи (несколько похожей на научную запись):

(-1)^s x 1.m x 2^(e-127), where
  s = sign of the number, range:{0,1} - takes up 1 bit
  m = mantissa (fractional portion) of the number - takes up 23 bits
  e = exponent of the number offset by 127, range:{0,..,255} - takes up 8 bits

и затем сохраняется в памяти как

0th byte 1st byte 2nd byte 3rd byte
mmmmmmmm mmmmmmmm emmmmmmm seeeeeee

Поэтому десятичное число 125.1 сначала преобразуется в двоичную форму, но ограничено 24 битами, так что мантисса представлена ​​не более чем 23 битами. После преобразования в двоичную форму:

125.1 = 1111101.00011001100110011

ПРИМЕЧАНИЕ: 0,1 в десятичном виде можно представить с точностью до бесконечных битов в двоичном формате, но компьютер ограничивает представление 17 битами, поэтому полное представление не превышает 24 бит.

Теперь, преобразовав его в указанную запись, мы получим:

125.1 = 1.111101 00011001100110011 x 2^6
      = (-1)^0 + 1.111101 00011001100110011 x 2^(133-127)

что подразумевает

s = 0
m = 11110100011001100110011
e = 133 = 10000101

Следовательно, 125.1 будет сохранено в памяти как:

0th byte 1st byte 2nd byte 3rd byte
mmmmmmmm mmmmmmmm emmmmmmm seeeeeee
00110011 00110011 11111010 01000010

После передачи в функцию printf () поток вывода генерируется путем преобразования двоичной формы в десятичную. Байты фактически сохраняются в обратном порядке (из входного потока) и, следовательно, читаются в следующем порядке:

3rd byte 2nd byte 1st byte 0th byte
seeeeeee emmmmmmm mmmmmmmm mmmmmmmm
01000010 11111010 00110011 00110011

Далее он преобразуется в специальную запись для преобразования

(-1)^0 + 1.111101 00011001100110011 x 2^(133-127)

Об упрощении вышеприведенного представления:

= 1.111101 00011001100110011 x 2^6
= 1111101.00011001100110011

И, наконец, преобразование его в десятичное число:

= 125.0999984741210938

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

2 голосов
/ 30 июня 2011

Сначала подумайте о представлении с фиксированной точкой.

2^3=8 2^2=4 2^1=2 2^0=1 2^-1=1/2 2^-2=1/4 2^-3=1/8 2^-4=1/16

Если мы хотим представить дробь, то мы устанавливаем биты справа от точки, поэтому 5.5 представляется как 01011000.

Но если мы хотим представить 5.6, точного дробного представления не существует. Самое близкое, что мы можем получить, это 01011001 == 5.5625

1/2 + 1/16 = 0.5625

2^-4 + 2^-1

0 голосов
/ 30 июня 2011

Обычный тип, используемый для числа с плавающей запятой в C - это double, а не float.Ваше число с плавающей точкой неявно приводится к двойному значению, и, поскольку число с плавающей точкой является менее точным, разница с ближайшим представимым числом до 125,1 является более очевидной (а точность печати по умолчанию специально для использования с двойными числами).Попробуйте вместо этого:

#include<stdio.h>
int main(void)
{
    double f;
    printf("\nInput a floating-point no.: ");
    scanf("%lf",&f);
    printf("\nOutput: %f\n",f);
    return 0;
}
0 голосов
/ 30 июня 2011

Числа с плавающей запятой не имеют точного представления, все они имеют ограниченную точность. При преобразовании числа в тексте в число с плавающей запятой (с помощью scanf или иным способом) вы попадаете в другой мир с другими типами чисел, и точность может быть потеряна. То же самое происходит при преобразовании из числа с плавающей точкой в ​​строку: вы решаете, сколько цифр вы хотите. Вы не можете знать «сколько цифр» в плавающей запятой до преобразования в текст или другой формат, который может хранить эту информацию. Это все связано с как хранятся числа с плавающей запятой :

significant_digits * baseexponent

0 голосов
/ 30 июня 2011

Если я скажу вам записать 1/3 как десятичное число вниз, вы поймете, что числа не имеют конечного представления..1 является точным представлением 1/10, там эта проблема не появляется, НО это только в десятичном представлении.В двоичном представлении .1 является одним из тех чисел, которые требуют бесконечных цифр.Поскольку ваш номер должен быть сокращен, значит, что-то потеряно.

0 голосов
/ 30 июня 2011

Поскольку это самое близкое представление 125.1, помните, что с плавающей точкой одинарной точности всего 32 бита.

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