Почему printf() не выводит это целое число как число с плавающей запятой?
У меня есть этот код:
#include <stdio.h>
int main()
{
int i = 12345;
printf("%f", i);
return 0;
}
printf() выведет 0.000000, не должен printf() интерпретировать биты, содержащиеся в i
как число с плавающей точкой?
8 ответов
Это технически неопределенное поведение, поэтому может случиться что угодно, например, то, что @Art описывает в своем ответе.
Таким образом, этот ответ является объяснением того, что произойдет, если вы попытаетесь напечатать 12345 как число с плавающей точкой, предполагая, что printf действительно видит правильное 32-битное значение.
Давайте проанализируем двоичное представление для числа, которое вы пытаетесь представить.
Используя http://www.h-schmidt.net/FloatConverter/IEEE754.html мы видим, что десятичное число 12345
имеет следующее 32-битное представление:
decimal 12345
hexadecimal 0x00003039
Преобразованный бит в бит в 32-битное значение с плавающей точкой IEEE-754, это представляет:
float 1.7299E-41
давайте попробуем напечатать это:
#include <stdio.h>
int main() {
printf("%f\n", 1.7299E-41);
return 0;
}
Это печатает:
0.000000
Теперь давайте прочитаем printf
man-страница, найденная здесь: http://linux.die.net/man/3/printf
F, F
Двойной аргумент округляется и преобразуется в десятичную запись в стиле [-]ddd.ddd, где количество цифр после символа десятичной точки равно спецификации точности. Если точность отсутствует, она принимается за 6; если точность явно равна нулю, символ десятичной точки не появляется. Если появляется десятичная точка, перед ней должна быть хотя бы одна цифра.
1.7299E-41
Значение не может быть представлено с использованием этого флага с точностью по умолчанию, поэтому полученный результат действительно является правильным значением.
Технически это неопределенное поведение.
Вы должны использовать союз, чтобы сделать это и распечатать его с %e
вместо %f
потому что это небольшое число:
#include <stdio.h>
union int2float
{
int a;
float b;
};
int main()
{
union int2float tmp;
tmp.a = 12345;
printf("%e\n", tmp.b);
return 0;
}
Это приводит к 1.729903e-41
,
с %.100f
ты получишь 0.0000000000000000000000000000000000000000172990295420898667405534417057140146406548336724655872023410
в качестве вывода.
Наиболее вероятная причина printf
не интерпретирует биты в i
в качестве числа с плавающей запятой является то, что printf
не видит i
совсем. Я подозреваю, что вы используете x86_64 или аналогичную платформу, где аргументы передаются функциям в регистрах. Обычно printf
интерпретировал бы ваш int как любой спецификатор формата, который вы ему дали. Но аргументы с плавающей точкой для функций обрабатываются по-разному в x86_64 и помещаются в разные регистры. Таким образом, причина, по которой вы получаете 0 как результат printf
извлекает первый аргумент с плавающей запятой после строки формата вместо первого регистра общего назначения и печатает его. Поскольку вы не использовали никаких регистров с плавающей запятой до вызова printf
они, скорее всего, обнуляются с момента запуска программы.
Вот эксперимент, который вы можете попробовать:
#include <stdio.h>
int
main(int argc, char **argv)
{
printf("%f\n", 17.42);
printf("%f\n", 0x87654321);
return 0;
}
Я получаю этот вывод:
$ ./foo
17.420000
17.420000
Сказав все это, единственный правильный ответ: это неопределенное поведение, не делайте этого. Но интересно посмотреть, что происходит под капотом.
Если вы хотите вникнуть в это и запустить Linux/MacOS/*BSD, этот документ, раздел 3.2.3 описывает, как аргументы передаются функциям (включая функции varargs, такие как printf). Windows делает вещи немного по-другому, но в этом случае результат на Windows будет точно таким же. printf
в Linux/MacOS/*BSD ожидает, что аргумент будет напечатан в %xmm0
(%xmm1
в Windows), пока ваш вызов передает его в %rsi
(%rdx
на винде).
Конечно, если я ошибаюсь в x86_64, не обращайте внимания на все, что я сказал, и посмотрите на ответ @SirDarius, потому что это относится ко многим (особенно старым) архитектурам. Если вы используете альфа, sparc, powerpc, arm и т. Д., То вы сами по себе. Вызов printf
один раз с int, один раз с плавающей точкой, разберите скомпилированный код и посмотрите, как передаются аргументы.
Если вы хотите напечатать int как float, приведите его:
printf("%f", (float)i);
Единственный способ printf()
знает тип данных аргументов после строки форматирования (и их количества) путем чтения строки формата. Представление целого числа в памяти отличается от числа с плавающей запятой. Вы передаете данные printf() в формате целого числа, но он принимает формат числа с плавающей запятой. Вот почему вы получаете мусор в консоли. Вы можете исправить это путем приведения типов.
printf("%f", (float) i);
Когда вы используете %f
спецификатор формата, вы должны использовать переменную float и распечатать значение. но вы объявили i
как целое число и пытается печатать, используя %f
это напечатает некоторое значение. Поскольку это целое число, вы должны использовать %d
спецификатор. В printf %f
будет искать переменную с плавающей точкой. но не будет переменной с плавающей точкой, поэтому она печатает некоторое значение.
Вы можете набрать целое значение во время печати.printf("%f",(float)i);
Делать i
как float
Тип, вам нужно сделать приведение типа.
Ты можешь использовать
printf("%f", (float)i);
Пример.
float myFloat;
int myInt;
myFloat = (float)myInt ; // Type casting