2013-12-09 2 views
7

Может быть, я не понимаю, стандарта IEEE754, что много, но, учитывая множество значений с плавающей точкой, которые float или double, например:Как правильно нормализовать значение с плавающей запятой в C++?

56.543f 3238.124124f 121.3f ... 

вы можете преобразовать их в значении от 0 до 1, поэтому вы их нормализуете, используя соответствующий общий коэффициент при рассмотрении того, что является максимальным значением и минимальным значением в наборе.

Теперь я хочу сказать, что в этом преобразовании мне нужна намного более высокая точность для набора целей, который колеблется от 0 до 1 по сравнению с уровнем точности, который мне нужен в первом, особенно если значения в первый набор охватывает широкий диапазон численных значений (действительно больших и очень малых значений).

Как float или тип double (или стандарт IEEE 754, если хотите) могут справиться с этой ситуацией, обеспечивая при этом более высокую точность для второго набора значений, зная, что мне в основном не нужна целая часть?

Или это совсем не так, и мне нужна математика с фиксированной точкой с совершенно другим типом?

+2

Обратите внимание, что float ieee не является обязательным в C++ – PlasmaHH

+0

@PlasmaHH: Есть ли компилятор, который не реализует IEEE 754? Или лучше поставить, какие современные архитектуры процессора не реализуют? – thokra

+1

@thokra: Обычно это не компилятор, его аппаратное обеспечение. В настоящее время я думаю о VAX. – PlasmaHH

ответ

5

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

Когда вы сжимаете свой набор значений с плавающей запятой до диапазона 0..1, единственная потеря точности, которую вы получите, будет вызвана округлением, которое происходит на разных этапах процесса.

Если вы просто сжатие за счет масштабирования, вы потеряете только небольшое количество точности вблизи LSBs мантиссы (около 1 или 2 ULP, где ULP означает «единиц на последнем месте).

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

Если вы выполняете всю арифметику с точностью double, вы можете нести 53 бит точности при вычислении. Если ваша точность должна соответствовать что (что, вероятно, они и делают), тогда все будет в порядке. В противном случае точная численная производительность будет зависеть от распределения ваших данных.

+0

«из-за округления, которое происходит» да, конечно, но у меня также есть бит в заданном типе с плавающей точкой, который будет не использован, поэтому я округляю свои нормализованные значения, растрачивая биты, вы можете видеть мою точку зрения? – user2485710

+0

В 64-битном двоичном коде IEEE 754 каждый бит-шаблон от 0x0 до 0x3ff0_0000_0000_0000 представляет собой отличное число в [0,1], поэтому вы теряете меньше 3 бит из 64. Меня больше беспокоит округление. –

+0

@PatriciaShanahan: Это 10 бит – Moberg

3

Одиночные и двойные поплавки IEEE имеют формат, в котором части экспоненты и доли имеют фиксированную ширину бита. Таким образом, это невозможно (т. Е. У вас всегда будут неиспользуемые биты, если вы сохраняете только значения от 0 до 1). (См.: http://en.wikipedia.org/wiki/Single-precision_floating-point_format)

Вы уверены, что 52-разрядная фрактальная часть двоеточия недостаточно точна?

Редактировать: Если вы используете весь диапазон плавающего формата, вы потеряете точность при нормализации значений. Округления могут быть выключены, и достаточно малых значений станет 0. Если вы не знаете, что это проблема, не беспокойтесь. В противном случае вам нужно найти другое решение, как указано в других ответах.

+0

Проблема заключается в понимании того, насколько велика максимальная величина и насколько мала наименьшая величина, если в них достаточно разницы, 52-битная часть двойника может быть проблемой, да. Именно по этой причине я хотел бы не тратить ни одного бита в тип, поэтому я могу извлечь максимальную пользу из этих 64 бит. – user2485710

+0

Да, это может быть проблемой, если у вас такой широкий диапазон данных и вам не нужно терять никаких данных. Это так? Вы можете использовать http://www.cplusplus.com/reference/limits/numeric_limits/, чтобы проверить, опуститесь ли вы ниже минимума (= 0). Деление с плавающей запятой почти всегда имеет ошибку округления. – Moberg

2

Для повышения точности вы можете попробовать http://www.boost.org/doc/libs/1_55_0/libs/multiprecision/doc/html/boost_multiprecision/tut/floats.html.

Заметим также, что для численного критической операции +, - существуют специальные алгоритмы, которые сводят к минимуму численное ошибке, допущенной по алгоритму:

http://en.wikipedia.org/wiki/Kahan_summation_algorithm

2

Если у вас есть выбор double с и вы нормализуют их между 0.0 и 1.0, существует ряд источников точных потерь. Однако все они намного меньше, чем вы подозреваете.

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

Во-вторых, компонент экспоненты больше не будет использовать возможность положительного экспонента.

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

Forth, если входное пространство не содержит + inf или -inf или + NaN или -NaN или тому подобное, эти кодовые точки также будут потрачены впустую.

Но, по большей части, вы будете тратить около 3 бит информации в 64-разрядной версии double в свою нормализацию, одним из которых является то, что почти неизбежно, когда вы имеете дело с конечной шириной значения.

Любое 64-битное представление неподвижной точки значений от 0 до 1 будет иметь гораздо меньший «диапазон», чем double с. A double может представлять что-то порядка 10^-300, в то время как 64-битное представление фиксированной точки, которое включает в себя 1.0, может начинаться с 10^-19 или около того. (64-битное представление с фиксированной точкой может представлять 1 - 10^-19 как отличное от 1, тогда как double не может, но значение 64-битной фиксированной точки не может представлять ничего меньшего, чем 2^-64, тогда как double s может).

Некоторые из приведенных выше цифр являются приблизительными и могут зависеть от округления/точного формата.

2

Имея значение точки двоичных плавающей (с неявной ведущей) выражается как

(1+fraction) * 2^exponent where fraction < 1 

Разделения а/Ь:

a/b = (1+fraction(a))/(1+fraction(b)) * 2^(exponent(a) - exponent(b)) 

Следовательно, деления/умножение не имеет по существу без потери точности.

Вычитание а-Ь:

a-b = (1+fraction(a)) * 2^(exponent(a) - (1+fraction(b)) * exponent(b)) 

Следовательно, вычитание/добавление может иметь потери точности (большой - крошечное == большой)!

Зажимного значение х в интервале [Min, Max] на [0, 1]

(x - min)/(max - min) 

будет иметь проблемы точности, если какое-либо вычитание имеет потери точности.

Ответ на ваш вопрос: Ничего, выберите подходящее представление (с плавающей точкой, дробью, многоточность ...) для ваших алгоритмов и ожидаемых данных.

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