переменные с плавающей запятой в C [дубликаты]

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

#include<stdio.h>

int main ()
{
    int a = 9/5;
    printf("%f\n", a);
    return 0;
}

Выход:

0.000000

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


person Ram    schedule 25.10.2012    source источник
comment
Целочисленное деление... Несоответствие спецификатора формата printf...   -  person Mysticial    schedule 25.10.2012
comment
вы также должны проверить здесь: stackoverflow .com/questions/11673503/   -  person Azodious    schedule 25.10.2012
comment
Я не хочу исправлять здесь, я знаю об этом, Просто хочу знать, почему и что происходит в этом случае!   -  person Ram    schedule 25.10.2012


Ответы (5)


int a = 9/5;

выполняет целочисленное деление и игнорирует остаток, поэтому a устанавливается равным 1. Попытка напечатать это с использованием %f приводит к неопределенному поведению, но случайно вы получили 0.000000.

Do

double a = 9./5.;

вместо этого или напечатайте с %d, если целочисленное деление было желаемым поведением. (float также будет работать, но a будет повышен до double при передаче в printf, поэтому нет причин не использовать double.)

person Fred Foo    schedule 25.10.2012
comment
Частное войдет в a, а не остаток. - person Manik Sidana; 25.10.2012
comment
Таким образом, следует установить значение 1, не так ли? - person Ram; 25.10.2012
comment
@ManikSidana: да, поэтому он игнорирует остаток. - person Fred Foo; 25.10.2012
comment
9/5 - это 1, а не 0... это может привести к отрицательному результату. - person Aniket Inge; 25.10.2012
comment
@Ram: да, 1, извини. Тот факт, что он кажется равным 0, связан с тем, что вы передаете неверно введенный аргумент для спецификатора формата %f. - person Fred Foo; 25.10.2012
comment
@Aniket: думаю с моей стороны, исправлено. Но не важно, каков результат деления; передача int в printf, когда ожидается double, является UB. - person Fred Foo; 25.10.2012
comment
@ Ларсманс. Остаток получается с использованием %, а не / - person Manik Sidana; 25.10.2012
comment
@ManikSidana: я знаю это. Я говорю, что остаток игнорируется, поэтому возвращается только частное. - person Fred Foo; 25.10.2012
comment
Вы уверены, что после C99 при передаче аргумента функции происходит продвижение float до double? - person codewarrior; 25.10.2012
comment
@codewarrior: есть в случае varargs. - person Fred Foo; 25.10.2012

Это Неопределенное Поведение.

Вы используете спецификатор формата float (%f) для печати int (a). Вы должны использовать %d, чтобы увидеть правильный вывод.

person Azodious    schedule 25.10.2012

Это неопределенное поведение в C. Используйте спецификатор формата %d вместо %f.

Зависит ли printf() от порядка спецификаторов формата? дает вы подробный ответ.

person Jeyaram    schedule 25.10.2012

Вот краткий анализ вашего кода:

int a = 9/5; // 9/5 = 1.8, but since you are doing integer division and storing the value in an integer it will store 1.
printf("%f\n", a);//Using incorrect format specifiers with respect to datatypes, will cause undefined behavior

printf("%d\n",a);//This should print 1. And correct.

Или, если вы хотите поплавок:

вместо int используйте float :

   float a=9.0f/5;//This will store 1.800000f
   //float a=9/5 will store 1.000000 not, 1.8 because of integer divison 
   printf("%f\n",a); //This will print 1.800000

Также прочтите это: http://en.wikipedia.org/wiki/IEEE_754-2008 о том, как работают с плавающей запятой.

Уточнение о целочисленном делении:

C99: 6.5.5 Мультипликативные операторы

6 При делении целых чисел результатом оператора / является алгебраическое частное с отбрасыванием любой дробной части.88) Если частное a/b представимо, выражение (a/b)*b + a%b должно быть равно a

88) Это часто называют «усечением до нуля».

person askmish    schedule 25.10.2012
comment
9/5 дает целочисленное деление. Вы наверное имели ввиду 9.0/5.0f - person Aniket Inge; 25.10.2012
comment
9/5 — это не 1,8, а 1, поскольку 9 — это int, а 5 — это int. Неважно, пытаетесь ли вы сохранить 1 в int или float, это не влияет на вычисление. - person Lundin; 25.10.2012
comment
P.S: О мультипликативных операторах 6 При делении целых чисел the result of the / operator is the algebraic quotient with any fractional part discarded.88) Если частное a/b представимо, выражение (a/b)*b + a%b должно равняться a. И я должен сказать, что целое число является результатом из-за усечения в сторону нуля. - person askmish; 25.10.2012

Простое предположение, что ваше деление должно привести к дроби в обычной математике, не сделает ее волшебным образом плавающей.

int a = 9/5;

Вам нужно использовать

float a = 9.0/5;

Во-первых, вам нужен правильный тип данных, т. е. float (или еще лучше double для более высокой точности) для хранения float.

Во-вторых, если вы делите два целых числа, то есть 9 и 5, оно будет просто следовать целочисленному делению, т.е. сохранять только целую часть деления и отбрасывать остальные. Чтобы избежать этого, я добавил .0 после 9. Это заставит компилятор неявно преобразовать в float и выполнить деление.

Что касается вашего упоминания о том, почему он печатает 0, уже упоминалось, что попытка %f для целого числа является неопределенным поведением. Технически число с плавающей запятой состоит из 4 байтов, содержащих 3 байта числа и 1 байт экспоненты, и они объединяются для создания результирующего значения.

person fkl    schedule 25.10.2012
comment
9/5 на самом деле 1. Так что это не то, что он ищет. Почему преобразование с плавающей запятой в целое имеет значение 0,00 для этой конкретной проблемы - person Aniket Inge; 25.10.2012
comment
9/5 на самом деле 1 это очевидно. Я вставил его из вопроса и добавил под ним правильную версию, чтобы прояснить разницу. Где я сказал, что он искал 1?? - person fkl; 25.10.2012
comment
Строго говоря, вы должны писать либо float a = 9.0f / 5.0f, либо double a = 9.0 / 5.0. Когда вы написали этот пример, вы принудительно выполняете расчет для двойного числа, которое затем усекаете до числа с плавающей запятой. Вы делаете код более неэффективным без какой-либо выгоды. Если вам повезет, компилятор может быть достаточно умен для оптимизации, но не рассчитывайте на это. - person Lundin; 25.10.2012
comment
Это правильно, но очень «строго говоря». Учитывая человека, задавшего вопрос. Предпочтение double или float должно оставаться в стороне. Как учитель, я всегда учился иллюстрировать, но не усложнять тему. Даже если его понизить до числа с плавающей запятой, он достигает цели генерации значения с плавающей запятой, хотя и с меньшей точностью. Я, конечно, не вижу в этом причины для отрицательного голосования и не считаю выше лучшее объяснение. Но у каждого свое мнение :) - person fkl; 25.10.2012