печать int с использованием спецификатора формата %f

Я хотел знать, почему вывод идет как 0.000000. Я знаю, что печать int с использованием спецификатора формата float или использование типа значения с использованием типа b дает неопределенное поведение, как написано во многих местах. я это цитирую

«Если спецификация преобразования недействительна, поведение не определено. Если какой-либо аргумент не является правильным типом для соответствующей спецификации преобразования, поведение не определено».

Но он всегда дает 0,000000, должно быть что-то определенное либо в регистре, либо в ассемблерном коде.

Короче говоря, я хотел знать, почему вывод всегда равен 0,000000 для любого значения i?

#include<stdio.h>
int main()
{
    int i=10;
    printf("%f\n",i);
    return 0;
}

Все сказали, что я знаю, что значение i может быть где-то в выводе, но из-за размера окна прецессии оно не отображается на консоли. Если я использую %e, я получаю введите здесь описание изображения


person HimanshuArora9419    schedule 09.07.2015    source источник
comment
Вы хотите копаться в UB? Хорошо, сделай это на своей коробке. Пройдитесь по нему с помощью отладчика, отметив целочисленный формат и формат IEEE FP в вашей системе, и выясните, что именно происходит. Однако я не вижу особой пользы в хранилище знаний SO:   -  person Martin James    schedule 09.07.2015
comment
Я голосую за то, чтобы закрыть этот вопрос как не по теме, потому что он пытается объяснить UB.   -  person Martin James    schedule 09.07.2015
comment
Это может быть связано с тем, что %f ожидает значение с плавающей запятой в регистре с плавающей запятой, а поскольку его нет, отображается 0.0000. (Результатом попытки чтения из неинициализированного регистра с плавающей запятой также является Undefined Behavior.)   -  person Jongware    schedule 09.07.2015
comment
Используйте "%e" вместо "%f". Это даст больше понимания. Около половины всех double печатают 0,000000, так как это значение очень мало. При использовании "%e" подозрительные изменения на i проявятся сами собой.   -  person chux - Reinstate Monica    schedule 09.07.2015
comment
Будьте более осторожны в изложении того, что вы видите. Конечно, вывод может быть 0,000000 (6 нулей после «.»). Почему тогда вы всегда указываете 0,0000000 (7 нулей после «.») и всегда 0,00000000 (8 нулей после «.»)? Внимание к деталям важно при решении любой проблемы.   -  person chux - Reinstate Monica    schedule 09.07.2015
comment
Отмечать этот вопрос как дубликат нехорошо. То, что сказал Мартин Джеймс, кажется хорошей причиной для закрытия этого вопроса, но дублирует.   -  person HimanshuArora9419    schedule 09.07.2015
comment
Короткий ответ заключается в том, что битовая комбинация в i интерпретируется как 64-битное двойное число, и, учитывая распространенные представления с плавающей запятой, будет соответствовать чему-то в окрестности 2^-47, то есть от 0 до что-то вроде 13 или 14 знаков после запятой (при условии, что вы используете x86).   -  person John Bode    schedule 09.07.2015
comment
Однако я также должен отметить, что, как правило, выяснять, почему вы получаете конкретный результат для неопределенного поведения, пустая трата времени, поскольку повторяемость результата не гарантируется. Главный урок, который следует извлечь из этого, — не делайте этого.   -  person John Bode    schedule 09.07.2015
comment
Передача параметров в стеке, через регистры и т. д. не определяется C. Определенно код пытается передать int (вероятно, 32-битный), а printf() ищет double (вероятно, 64-битный). Скорее всего не совпадают размеры. Учитывая вывод "6.952880e-308", который может иметь шестнадцатеричный шаблон 0028FF8FE2FFCBFD и совсем не похож на i = 0x0000000A, я подозреваю, что несоответствие в способе передачи данных также способствовало необычному результату. Чтобы узнать, вы можете начать сбрасывать ассемблерный код и анализировать его, чтобы определить, какое неопределенное поведение произошло, но это работа и сомнительная ценность.   -  person chux - Reinstate Monica    schedule 09.07.2015


Ответы (6)


Вы можете получить те же результаты в течение дня и другой результат на следующий день, вот и вся история о undefined поведение. Стандарт не гарантирует какого-либо детерминированного результата для неопределенного поведения.

Вы должны использовать правильные спецификаторы типа при печати с использованием printf

person Steephen    schedule 09.07.2015
comment
В этом случае это будет то же самое, если вы даже попробуете это на всю жизнь. - person HimanshuArora9419; 09.07.2015

Неопределенное поведение не означает, что вы должны получать разные результаты в разных запусках. Это означает только то, что вы не должны ожидать какого-либо четко определенного поведения.

person dlask    schedule 09.07.2015

Используйте
printf("%f\n",(float)i);

Компилятор автоматически не переводит ваше int в float

Изменить: я думаю, что это интересный вопрос, поэтому я нашел похожую статью: Код функции printf на C

Затем я попытался изучить __printf , vfprintf и ___printf_fp.

__printf (const char *format, ...)
{
   va_list arg;
   int done;

   va_start (arg, format);
   done = vfprintf (stdout, format, arg);
   va_end (arg);

   return done;
}

В vfprintf определяется таблица jump_table, которая обрабатывает определенный формат (%d, %f, %x...)

static const uint8_t jump_table[] =
 {
   /* ' ' */  1,            0,            0, /* '#' */  4,
              0, /* '%' */ 14,            0, /* '\''*/  6,
              0,            0, /* '*' */  7, /* '+' */  2,
              0, /* '-' */  3, /* '.' */  9,            0,
   /* '0' */  5, /* '1' */  8, /* '2' */  8, /* '3' */  8,
   /* '4' */  8, /* '5' */  8, /* '6' */  8, /* '7' */  8,
   /* '8' */  8, /* '9' */  8,            0,            0,
              0,            0,            0,            0,
              0, /* 'A' */ 26,            0, /* 'C' */ 25,
              0, /* 'E' */ 19, /* F */   19, /* 'G' */ 19,
              0, /* 'I' */ 29,            0,            0,
   /* 'L' */ 12,            0,            0,            0,
              0,            0,            0, /* 'S' */ 21,
              0,            0,            0,            0,
   /* 'X' */ 18,            0, /* 'Z' */ 13,            0,
              0,            0,            0,            0,
              0, /* 'a' */ 26,            0, /* 'c' */ 20,
   /* 'd' */ 15, /* 'e' */ 19, /* 'f' */ 19, /* 'g' */ 19,
   /* 'h' */ 10, /* 'i' */ 15, /* 'j' */ 28,            0,
   /* 'l' */ 11, /* 'm' */ 24, /* 'n' */ 23, /* 'o' */ 17,
   /* 'p' */ 22, /* 'q' */ 12,            0, /* 's' */ 21,
   /* 't' */ 27, /* 'u' */ 16,            0,            0,
   /* 'x' */ 18,            0, /* 'z' */ 13
 };

Затем они помещают LABEL в эту функцию vfprintf (что-то вроде случая переключения)

int vfprintf (FILE *s, const CHAR_T *format, va_list ap)
{
...
LABEL (form_percent):           
  /* Write a literal "%".  */   
  outchar (L_('%'));            
  break;                        

LABEL (form_integer):           
  /* Signed decimal integer.  */
  base = 10;

LABEL (form_float): 
...
}

В Label form_float они определяют структуру, определяющую формат float.

struct printf_info info = { 
.prec = prec,                    
.width = width,                  
.spec = spec,                    
.is_long_double = is_long_double,
.is_short = is_short,            
.is_long = is_long,              
.alt = alt,                      
.space = space,                  
.left = left,                    
.showsign = showsign,            
.group = group,                  
.pad = pad,                      
.extra = 0,                      
.i18n = use_outdigits,           
.wide = sizeof (CHAR_T) != 1 
}; 

Наконец, они звонят

int ___printf_fp (FILE *fp, 
                  const struct printf_info *info,
                  const void *const *args)

для печати вывода.

Вывод: если входной формат неверный, у нас будет неверный формат struct info, то и вывод тоже должен быть неверным.

person Ngo Thanh Nhan    schedule 09.07.2015

Вы помещаете целое число в стек, но сообщаете printf, что это число с плавающей запятой.

Вот правильный пример:

#include<stdio.h>
int main()
{
  int i=10;
  printf("%f\n", (float)i);
  return 0;
}

Добавление:

Существует разница в представлении int и float при передаче в виде varargs. В большинстве ABI float передается как double, поэтому он в два раза длиннее, чем int (при условии, что int имеет размер 4, а double 8). Поэтому вы можете поэкспериментировать с типом данных int64_t. В этом случае вы можете увидеть разные значения, если вы соответствуете двоичному представлению некоторого значения с плавающей запятой в целое число.

person Valeri Atamaniouk    schedule 09.07.2015
comment
Я знаю, что такое правильный формат. Я хотел знать, что на самом деле происходит при чтении значения int с использованием спецификатора формата float. - person HimanshuArora9419; 09.07.2015
comment
Я просто хотел знать, почему это происходит, почему значение всегда равно 0.0000000 . - person HimanshuArora9419; 09.07.2015
comment
Потому что числа с плавающей запятой сильно отличаются от целых чисел в двоичной форме. См. stackoverflow.com /вопросы/9643643/ - person Valeri Atamaniouk; 09.07.2015

Короче говоря, я хотел знать, почему вывод всегда будет 0.00000000 для любого значения i?

Короче, написав

  printf("%f\n",i);

то есть печать int с использованием %f является неопределенным поведением. Вывод может быть любым, даже ваш номер телефона или PIN-код банкомата.

Процитирую стандарт (который вы уже знаете) с выделением с моей стороны.

Если какой-либо аргумент не соответствует типу соответствующей спецификации преобразования, поведение не определено.

person Sourav Ghosh    schedule 09.07.2015
comment
Тогда то, что может произойти, объясняется здесь; это действительно плохие вещи! - person Basile Starynkevitch; 09.07.2015
comment
@BasileStarynkevitch Хорошо !!! Я хочу, чтобы программа с УБ заказала мне пиццу, а также заплатила за нее (не используя номер моей кредитной карты). - person Sourav Ghosh; 09.07.2015
comment
Нет, следует ожидать, что UB всегда будет плохим. Он не может производить хорошие вещи (а если и производил, то вероятность этого ничтожно мала). - person Basile Starynkevitch; 09.07.2015
comment
@BasileStarynkevitch с этой точки зрения, это действительно очень плохо для человека, чей номер карты используется. :П - person Sourav Ghosh; 09.07.2015
comment
Я действительно хотел бы знать причину отрицательного голоса .... - person Sourav Ghosh; 09.07.2015

Функция printf использует format specifiers, чтобы выяснить, что выталкивать из стека. Поэтому, когда он видит %f, интерпретирует их как double.

Поскольку здесь, в вашем коде, поведение types are not compatible (as promoted according to the default argument promotions) не определено.

person ameyCU    schedule 09.07.2015
comment
@chux Да, ты прав, моя ошибка. Я буду редактировать его. - person ameyCU; 09.07.2015