2012-04-28 2 views
7

Во-первых, это не вопрос точности или что-то в этом роде.Представление двоичных чисел

Вопрос в том, как компилятор решает, как представлять число?

Давайте возьмем C, например. Я пишу

double d = 4.5632; 

Как выбрать его двоичное представление? Я знаю, что он не представлен точно, так как он выбирает ближайший представимый номер? Это делается во время компиляции? Это делается процессором или ОС?

Пожалуйста, ответьте только в том случае, если вы знаете, как это происходит, ответы типа «не беспокойтесь об этом» не помогают. Кроме того, «это зависит от платформы» тоже не полезно, вы можете выбрать платформу и объяснить это.

+3

Компьютеров не «подцепить» бинарное представления - аппаратные дизайнеров и компиляторы делают. Взгляните на [этот стандарт] (http://en.wikipedia.org/wiki/IEEE_754-2008). – dasblinkenlight

ответ

6

Компилятор не принимает решения (обычно). ЦП (обычно) имеет блок с плавающей точкой, для которого значения с плавающей запятой должны быть представлены в определенном формате (обычно это IEEE-754). Конечно, можно эмулировать совершенно другую архитектуру, и в этом случае автор компилятора/эмулятора может выбрать совершенно другое представление. Но это не типично.

Что касается того, как конкретное лексическое представление 4.5632 преобразуется в базовое представление, указанное в стандарте C. Так из раздела 6.4.4.2 стандарта C99 (я выделил наиболее релевантную часть):

мантисса части интерпретируется как (десятичная или шестнадцатеричный) рационального число; последовательность цифр в экспоненциальной части равна , интерпретируемая как десятичное целое число. Для десятичных плавающих констант показатель указывает мощность 10, по которой значительная часть равна . Для шестнадцатеричных плавающих констант показатель показывает мощность 2, по которой значительная часть должна масштабироваться. Для десятичных чисел с плавающими констант, а также для шестнадцатеричного плавающей константы, когда FLT_RADIX не является степень 2, результатом является либо ближайшим представимым значением, или больше или меньше представимо значения непосредственно примыкающего к ближайшему представимому значению, выбираются в порядке реализации. Для шестнадцатеричных плавающих констант , когда FLT_RADIX имеет мощность 2, результат корректно округленный.

Это будет сделано во время компиляции (хотя стандарт не предусматривает этого).

+0

И это зависит от стандарта – nullpotent

+0

Это не отвечает на мой вопрос - как? Как он знает, что 3.14 должно быть представлено 0100111010 .... или что-то еще? – AMCoder

+0

@AMCoder: Я неправильно понял ваш вопрос. См. Обновление. –

0

Да, это конкретное преобразование выполняется во время компиляции, так как double d = 4.5632; является константой времени компиляции. То, что скомпилировано в ваш код, представляет собой представление этого значения в формате с плавающей запятой, используемом целевой архитектурой. В случае 32-битного представления IEEE-754 это 0x409205BC. Как ЦП «знает», что это значение, несколько близкое к 4.5632, зависит от самого стандарта с плавающей запятой. Опять же, в случае 32-разрядного IEEE-754 у нас есть один бит для знака, восемь бит для показателя и 23 бит для мантиссы.

Когда дело доходит до округления, существует несколько методов, которые можно применять. Спецификация IEEE-754 содержит четыре метода: от округлых до ближайших, от округлых до нуля, от округлой до отрицательной бесконечности, от округлой до положительной бесконечности.

0

Компилятор создает программу для работы на платформе. Возможно, платформа существовала перед компилятором или наоборот. Двоичные представления всего составляют ABI, который по сути является спецификацией выхода компилятора. В конце концов, все сделано, но по какой-то причине они сделаны, но, надеюсь, есть ABI, чтобы точно сказать, что происходит.

На практике практически все платформы реализуют арифметику с плавающей запятой в соответствии с IEEE 754, также известный как IEC 559. Этот довольно старый международный стандарт определяет, что означают биты числа с плавающей запятой и как должно быть округлено десятичное представление программы к значению с плавающей запятой.

Платформы без FPU по-прежнему обычно упаковывают и распаковывают битовые поля из номеров IEEE 754 в программном обеспечении, поскольку они, вероятно, будут отображаться в двоичной форме в файлах.

Платформы с ограниченными требованиями к совместимости и числовой точности, такие как графические процессоры, скорее всего, ослабят стандарт точности, требуемый IEEE 754, но числовые диапазоны, которые он определяет, являются наилучшими для широкого круга приложений.

Конечно, вы не можете зависеть ни на чем, если хотите предельную переносимость. Но безопасно полагать, что преобразование из десятичной в двоичную FP (предполагая, что сам FPU не является десятичным) выполняется во время компиляции.

0

Для вашего конкретного примера да, двоичное представление кодируется во время компиляции. Вероятно, он вызывает библиотеку C (atod, sscanf, etC), и то, что происходит в этой библиотеке с усечением или округлением, происходит. И компиляторы «функции» или «правила» для того, что он делает, не обязательно являются одними и теми же правилами выполнения, которые происходят, когда вы делаете то же самое. Вы никогда не должны проверять эквивалентность с плавающей точкой в ​​любом случае, но если вы должны взять значение времени компиляции, а затем подать программу в строку и преобразовать эту среду выполнения (скажем, вы передаете значение 4.5632 в командной строке и используете один из вызовов библиотеки) вы не обязательно получите одинаковое значение с плавающей запятой. Я видел, что компиляторы (gcc и т. Д.) Выполняют очень плохую работу с константами времени компиляции, поэтому, как правило, для такого числа, как ваше (не так много в мантиссе), я предпочитаю точность:

double d; int a; 
a 45632; 
d = a; 
d/=10000; 

И даже если он оптимизирует его, он стремится получить лучший, более точный ответ.

Вы выполняете риск ошибки аппаратного обеспечения + OS в int для двойного преобразования, Хаузер сделал несколько комментариев о ошибках FPU, которые стремятся быть в int для float и float для операций int. Даже если во время компиляции я бы предположил, что компилятор буквально сделает два int для float, тогда разделите, а не строку, чтобы плавать напрямую, как ваш код.

Прошло несколько лет с тех пор, как я продемонстрировал все это, возможно, компиляторы стали лучше (сомнительно). Надеемся, что оборудование стало лучше (вероятно, очень редко можно было найти fpu, без труда найти ошибки).

+0

Обычно используется формат с плавающей запятой IEEE 754. Но это зависит от аппаратного обеспечения, если в вашей системе есть аппаратный FPU, тогда любой формат, который использует оборудование, вероятно, компилятор компиляции. Если это мягкий fpu, то это любой формат, который требуется мягкому fpu. IEEE является более сложным/медленным/менее надежным форматом из-за множества функций. Формат ti dsp, например, значительно чище, быстрее, надежнее, но не имеет округления или бесконечности или nans. –

0

Ваш конкретный пример преобразуется компилятором, потому что это десятичный литерал. Вы хотите особенности, поэтому давайте возьмем gcc. Он делает преобразование в real.c (я не знаю, была ли это текущая версия, но это была первая копия, которую я нашел через Google), в функции, называемой real_from_string(). По сути, это конверсия с длинным разделением: в вашем случае - 45632/10000.

(Decimal для преобразования с плавающей точкой является довольно громоздок, проверить my blog, если вы хотите, чтобы узнать больше.)

Смежные вопросы