Можно напечатать любое целое значение , какое угодно, независимо от параметра с плавающей запятой:
printf("A: %d B: %6.2f\n", f, f + 0.15);
Вот как вы можете печатать произвольные целые числа на архитектуре Intel:
int print_it(int, int /* nameless but printed */, float f)
{
printf("A: %d B: %6.2f\n", f, f + 0.15);
}
int main()
{
print_it(0, 12 /* will be printed */, 0.0);
print_it(0, 123 /* printed */, 1.1);
print_it(0, 1234 /* printed */ , 2.2);
}
Этот вывод:
A: 12 B: 0.00
A: 123 B: 1.10
A: 1234 B: 2.20
Объяснение: Очевидно, что несовпадающая строка формата и параметры приводят к неопределенному поведению. Тем не менее, иногда это можно предсказать. В архитектуре Intel первые несколько параметров передаются регистрами. Значения с плавающей запятой передаются в разные регистры.
Несмотря на то, что инструкция printf
такая же, как и в вопросе, вывод отличается. Что происходит, так это то, что 12, 123, 1234 передаются через регистр общего назначения, отвечающий за второй параметр без плавающей запятой. Поскольку printf
имеет только один параметр без плавающей запятой, регистр второго параметра без плавающей запятой не изменяется. Этот регистр сохраняет значение, полученное из второго параметра print_it(0, int_value, fp_value)
.
Но оригинал дает мусор:
for (f = 0.0; f <= 3; f += 1.1)
printf("A: %3f B: %6.2f\n", f, f + 0.15);
Это дает различный мусор, потому что printf
вызывает другие функции внутри. Эти функции уничтожают регистр общего назначения, который читает printf("... %d ...", ...)
.
Очевидно, такое поведение имеет место только в системах, которые передают параметры с плавающей запятой в отдельный набор регистров. Очевидно, что это происходит только в том случае, если оптимизация компилятора не изменяет код каким-либо образом, потому что разрешено делать какие-то дикие вещи, когда происходит неопределенное поведение.