использование printf для вывода плавающих значений - PullRequest
3 голосов
/ 03 сентября 2011
#include<stdio.h>
#include<stdlib.h>

int main(void)
{
  int x, *ptr_x;
  float f , *ptr_f;

  ptr_f = &f;
  ptr_x = &x;
  *ptr_x = 5;
  *ptr_f = 1.5; //printf("%d %f\n", f,x);

  printf ("\n\nxd = %d \t xf = %f \n ff = %f \t fd = %d", x,x,f,f);
  return 0;
}

Выход для ff =% f не ожидается.

xd = 5 xf = 0.000000
ff = 0.000000 fd = 1073217536

Смысл этого кода в том, чтобы показать, что произойдет, если плавающее значение будет напечатано с помощью% d, и если значение int будет напечатано% f.

Почему значение с плавающей точкой печатается неправильно, даже если я использую% f?

Ответы [ 3 ]

15 голосов
/ 03 сентября 2011

printf () небезопасен.

Аргументы, которые вы передаете printf(), обрабатываются в соответствии с тем, что вы обещаете компилятору.

Кроме того, float s повышаются до double s, когда передаются через аргументы с переменными числами.

Таким образом, когда вы обещаете компилятору %f в первый раз (для xf), компилятор сглатывает целый double (обычно 8 байт) из аргументов, поглощая ваш float в процессе. Затем второй %f врезается прямо в нулевую мантиссу второго двойника.

Вот изображение ваших аргументов:

+-0-1-2-3-+-0-1-2-3-+-0-1-2-3-4-5-6-7-+-0-1-2-3-4-5-6-7-+
|    x    |    x    |        f        |        f        |
+---------+---------+-----------------+-----------------+

%d--------|%f----------------|%f---------------|%d------|

Но f выглядит так (после повышения до double):

f = 3FF8000000000000

Давайте нарисуем его снова со значениями и рассмотрим догадки вашего компьютера:

| 05000000 | 05000000 | 00000000 0000F83F | 00000000 0000F83F |
| %d, OK   | %f, denormal...    | %f, denormal...   | %d, OK  |

Обратите внимание, что 1073217536 равен 0x3FF80000.

3 голосов
/ 03 сентября 2011

Как только вы передаете хотя бы один неверный спецификатор формата printf (например, попытка вывести значение float с помощью %d или int с помощью %f), ваше вся программа облажается без возможности ремонта. Последствия таких разрушительных действий можно увидеть в любом месте программы. В вашем случае попытка напечатать что-то с неверным спецификатором формата привела к тому, что даже действительные спецификаторы формата перестали работать.

Говоря формально, вы написали программу, которая демонстрирует неопределенное поведение . Это может действовать абсолютно непредсказуемо. Вы сказали это сами

Смысл этого кода в том, чтобы показать, что произойдет, если плавающий значение печатается с% d, и если значение int печатается% f.

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

0 голосов
/ 03 сентября 2011

Попробуйте это:

printf("size of int = %d, size of float = %d, size of double = %d\n",
    sizeof(int), sizeof(float), sizeof(double));

Когда вы вызываете printf(), система помещает аргументы в стек. Таким образом, стек выглядит примерно так:

pointer to format string [probably 4 bytes]
x [probably 4 bytes]
x [probably 4 bytes]
f [probably 6 or 8 bytes]
f [probably 6 or 8 bytes]

Затем printf() выталкивает байты из стека при разборе строки формата. Когда он видит %d, он выдает достаточно байтов для целого числа, а когда он видит %f, он выдает достаточно байтов для числа с плавающей запятой. (На самом деле, числа с плавающей точкой удваиваются, когда они передаются в качестве аргументов функции, но важная идея состоит в том, что им требуется больше байтов, чем целых чисел.) Так что, если вы «врете» аргументы, это приведет к неправильному количеству байтов и вслепую. конвертируйте их согласно вашим инструкциям.

Таким образом, сначала будет выдано правильное количество байтов для xd, потому что вы правильно сказали, что x является целым числом.

Но тогда он выкинет достаточно байтов для числа с плавающей запятой, которое будет использовать второй x и часть первого f из стека и интерпретировать их как число с плавающей запятой для xf.

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

Наконец, он извлечет достаточно байтов для целого числа, которое будет использовать оставшуюся часть второго f, и интерпретирует их как целое число для fd.

Надеюсь, это поможет.

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