Я знаю, что использовал неправильный спецификатор формата в своей программе на C при чтении чисел с плавающей запятой в переменные int с использованием scanf("%f", &variable)
.
Я не знаю, как программа выдает большие случайные значения. Как входные значения преобразуются в эти случайные значения?
#include <stdio.h>
#include <stdlib.h>
// Width Height --> Floating Point
float findArea (float width, float height)
{
float area;
area = width* height;
printf("width:%f\n",width);
printf("height:%f\n",height);
return area;
}
int main()
{
int heightRectangle, widthRectangle;
float area;
printf("Enter width: ");
scanf("%f",&widthRectangle);
printf("Enter height: ");
scanf("%f", &heightRectangle);
area = findArea(widthRectangle,heightRectangle);
printf("The area of your given rectangle is: %f \n",area);
return 0;
}
Выход:
Enter width: 5.0
Enter height: 2.5
width:1084227584.000000
height:1075838976.000000
The area of your given rectangle is: 1166454293721513984.000000
Размер int
и float
в моей системе составляет 4 байта. Чтобы определить это, я использовал оператор sizeof
.
Информация о компиляторе:
Я использую онлайн-компилятор programiz
Ребята, я еще раз знаю, что использовал неправильный спецификатор формата. Я просто жду ответа, который объясняет, как значения 5.0 и 2.5 преобразуются в 1084227584 и 1075838976.
@JKR, печать чисел с плавающей запятой с помощью "%a"
и целых чисел с помощью "%x"
может облегчить просмотр общих (возможно, сдвинутых) битовых шаблонов.
Поиграйте с онлайн-конвертером IEEE-754, таким как этот, чтобы увидеть, как числа с плавающей запятой представляются в двоичном формате.
%f
в scanf("%f",&widthRectangle);
инструктирует scanf
преобразовать входные данные в схему кодирования, используемую для объекта float
, и сохранить биты, полученные в результате этого кодирования, в памяти, на которую указывает widthRectangle
.
Поскольку в main
widthRectangle
объявлен как int
, когда widthRectangle
используется в findArea(widthRectangle,heightRectangle)
, программа интерпретирует эти биты, используя схему кодирования, используемую для int
. Затем, поскольку объявлено, что findArea
имеет параметры float
, программа преобразует это значение в тип float
и передает его в findArea
.
Таким образом, значение, которое позже напечатается printf("width:%f\n",width)
, является результатом интерпретации кодировки 5
как float
с использованием декодирования int
.
Все, что мы представляем в компьютере, закодировано произвольным назначением того, какие биты представляют какое значение. Типы int
и float
используют разные правила для того, какие биты представляют какие значения.
Для типов int
обычно используется дополнение до двух. Сначала начнем с правил
для кодирования неотрицательного целого числа в двоичном формате:
Чтобы закодировать неотрицательное целое число x в w битах (w означает «ширина»):
Чтобы закодировать целое число x в дополнении до двух w-битов:
Для типов float
используется многочастное кодирование числа с отдельными частями знака, показателя и мантиссы. Чаще всего используется форматbinary32 IEEE-754:
Для x = 5 это работает:
Когда битовая строка 01000000101000000000000000000000 интерпретируется как int
, результат равен 1 084 227 584.
Мы также можем обратить это вспять:
В шестнадцатеричном формате 1 084 227 584 — это 40A00000. В 32-битном двоичном формате это 0 10000001 01000000000000000000000. Я вставил пробелы, чтобы показать, где мы разделяем число на части из-за схемы кодирования с плавающей запятой. Первый бит, 0, означает положительный результат. Следующие восемь битов, 10000001, представляют собой кодировку показателя степени (и один бит мантиссы, ведущую 1). В двоичном виде это будет число 129. Показатель степени с плавающей экспонентой равен этому числу минус 127, то есть оно равно 2. Последние 23 бита — это последние 23 бита мантиссы. Вместе с ведущим 1 битом они представляют двоичное число 1,01000000000000000000000, что равно 1,25 в десятичном виде. Таким образом, представленное число с плавающей запятой равно + 22 • 1,25, что равно 5.
Аналогично, 1 075 838 976 — это шестнадцатеричное число 40200000, двоичное 0 10000000 01000000000000000000000, которое отличается только тем, что поле показателя является двоичным 10000000, десятичным 128, поэтому оно представляет показатель степени 1. Представленное число с плавающей запятой равно + 21 • 1,25, то есть 2,5 .
Возможно, вы захотите упомянуть неопределенное поведение в качестве введения к вашему объяснению... ОП знает о неправильном спецификаторе преобразования и хочет получить подробное объяснение, которое вы предоставляете, но кажется важным указать, что такое поведение не гарантируется.
@chqrlie: Нет. Вопрос явно спрашивает, почему они получают наблюдаемые значения, и «неопределенное поведение» не отвечает на этот вопрос. Это не объяснение; неопределенное поведение — это отсутствие правил, а не наличие причины. В первом предложении автор заявляет, что знает, что использовал неправильный спецификатор. Они повторяют это в предпоследнем предложении. В последнем предложении они заявляют, что просто ожидают ответа, объясняющего наблюдаемые значения. Автор явно исключил другое обсуждение, и я ответил на вопрос в соответствии с их рекомендациями.
Почему такое поведение? Числа с плавающей запятой с типами данных с плавающей запятой кодируются с использованием стандарта кодирования IEEE-754 (формат одинарной точности).
Как работает IEEE-754 в деталях?
1.What is IEEE 754?
> It's a standard for how computers represent floating-point numbers. Both positive and negative
floating-point numbers.
2.Single Precision Basics
>Single precision means we use 32 bits (or 4 bytes) to represent a number.
3.The 32 Bits are Split Into Three Parts:
>Sign bit: 1 bit that shows if the number is positive or negative (0 for positive, 1 for negative).
>Exponent: 8 bits that help represent the range of the number.
>Mantissa (or Fraction): 23 bits that hold the actual digits of the number.
4.The Formula for the Number:
>The number is represented as:
[±]1.[mantissa]*2^(exponent-127)
>The exponent is stored with a bias of 127, which helps in handling both small and large numbers.
Дополнительную информацию о формате одинарной точности см. здесь.
ссылка:->iee-754
Объяснение:=
ширина = 5,0-> 101,0 высота=2,5->10,1
Single precision conversion of width and height
width=0 10000001 01000000000000000000000
Height=0 10000000 01000000000000000000000
Типы данных int и float используют 32-битную/4-байтовую компьютерную память для хранения данных.
Эти числа читались как числа с плавающей запятой, но интерпретировались как целые числа. Обычное целочисленное 32-битное преобразование:
Width =01000000101000000000000000000000 is 1084227584
Height=01000000001000000000000000000000 is 1075838976
ширина*высота=1166454293721513984
IEE --> IEEE (необходимо исправление в 3+ местах)
Все ответы верны, но ваш ответ было легко понять. поэтому я отмечаю это как правильное.
Предполагаемые базовые знания: двоичные/битовые представления целых чисел и чисел с плавающей запятой.
Это сводится к
bitstring(5.0) == 01000000101000000000000000000000 (base 2) == 1084227584 (base 10)
иbitstring(2.5) == 01000000001000000000000000000000 (base 2) == 1075838976 (base 10)
Когда вы вызываете scanf
со спецификатором формата %f
, он анализирует входные данные в представление IEEE 754 с одинарной точностью. Например. 5.0
будет преобразован в 01000000101000000000000000000000
. В вашем случае это точное значение, хранящееся в &widthRectangle
.
Но поскольку widthRectangle
на самом деле является int
, это битовое представление (пере)интерпретируется как целое число при переходе к findArea
. Таким образом, значение в конечном итоге будет соответствующим десятичным (как в базе 10) целым числом для 01000000101000000000000000000000
, то есть 1084227584
.
Обратите внимание, что (как также указывали другие) использование неправильных спецификаторов формата в целом приводит к неопределенному поведению, и, следовательно, вы можете наблюдать разные выходные данные разных компиляторов. В этом случае результат оказывается «логичным», вероятно, потому, что float
и int
имеют одинаковый размер и выравнивание. Но опять же, на это никогда нельзя полагаться, поскольку это UB.
Поведение программы неопределенно из-за неправильного спецификатора формата (или, скорее, неправильного типа, с помощью которого определены ваши переменные). Это означает, что вы можете наблюдать любой результат — ничего не гарантировано и не указано. Было бы интересно узнать, что именно происходит, чтобы вы видели то, что видите; однако. имейте в виду, что с другим компилятором или с тем же компилятором в немного другой среде ваши наблюдения в принципе могут сильно отличаться.