Как сделать мантиссу

Добавил пользователь Morpheus
Обновлено: 08.09.2024

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

В качестве чисел с плавающей точкой использован 32-х разрядный float.


Числа с фиксированной точкой.

Стандартная библиотека языка Си не предоставляет никаких средств для вывода чисел с фиксированной точкой. Зато есть вывод чисел с плавающей точкой — функции *printf. Можно преобразовать число с фиксированной в число с плавающей точкой и вывести его с помощью, например, sprintf. Первый наивный способ преобразовать фиксированную точку в плавающую, состоит в том, что целочисленное представление числа с фиксированной точкой непосредственно приводится к типу с плавающей точкой, после чего делится на целочисленное представление на единицы в формате с фиксированной точкой:

Тип double, а не float, в качестве типа с плавающей точкой использован потому, что sprintf принимает именно его. Для AVR это не имеет значения, так как float и double там одно и тоже.
Этот метод очень прост и быстр в реализации, к тому-же достаточно надёжен: меньше кода — меньше ошибок. На этом его преимущества заканчиваются и остаются недостатки:
— медленный: AVR 4200 тактов, STM32 — 4234 тактов в среднем;
— использует sprintf и тянет за собой всё её обвязку;
— использует целых две операции с плавающей точкой.

Следующий вариант — это преобразовывать целую часть отдельно по одному из методов из предыдущей статьи, а дробную — методом умножения на 10. Для преобразования целой части целесообразно взять самый быстрый метод — деление на 10 сдвигами и сложениями.

Получилось неплохо никаких внешних зависимостей и кода не много. На AVR этот метод работает в среднем за 490 тактов, на STM32 — за 148. Результат отличный — быстрее, чем 32-х битное целое преобразуется по методу быстрого деления.

Также для чисел с фиксированной точкой применим метод умножения на 10. Он почти ничем не отличается от аналогичного для целых чисел. Только на этот раз для умножения исходного числа на AVR-ках я применил не тупой цикл со сдвигами и сложениями, честное 64-битное умножение по алгоритму Д. Кнута. Дело в том, что встроенное 64-битное умножение в avr-gcc не оптимизировано и работает неприлично долго порядка 800 тактов. Реализация, приведённая ниже работает за чуть более 100 тактов. Функция mul32hu возвращает старшие 32 бита от произведения, которые получаются в переменной u32[1]. Если нужно полное 64-х битное произведение, то можно вытащить и младшую часть — она находится в переменной u32[0].

На STM32 имеется инструкция умножения 32x32=>64, по этому используем встроенное умножение.

Кстати, если из метода с умножением на 10 из предыдущей статьи (utoa_fract) выкинуть цикл умножения и заменить его на mul32hu, то он будет выполняться на AVR в среднем за 648 тактов, что делает его быстрее, чем метод вычитания степеней 10 (utoa_cycle_sub).

В самом методе, как и для целых чисел, сначала умножаем исходное число на множитель, который представляет из себя степень двойки, соответствующую той битовой позиции, где будут извлекаться цифры, делённую на степень 10, такую, чтоб не было переполнения. Затем в цикле извлекаются десятичные цифры с определённой битовой позиции, в данном случае с 29 бита, и число умножается на 10. После извлечения всех цифр производится коррекция коррекция старшей цифры, так как она может оказаться больше 10. Для этой коррекции в строковом буфере предусмотрен 1 символ, который инициализируется в '0'. После удаляются ведущие и завершающие нули.

На AVR этот метод работает в среднем за 604 такта, на STM32 — 236. Не плохо, но всё-же хуже чем у предыдущего метода. Однако, если дробная часть будет не 16 разрядов, как в примере, а занимать все 32 бита, то умножение будет не нужно и этот метод станет проще и быстрее предыдущего.
Внимательный читатель заметит эту конструкцию.

Почему нельзя оставить только закомментированный фрагмент?
Потому, что в avr-gcc сдвиг 32-х разрядного числа плохо оптимизирован. Вместо того, чтоб взять старший байт и сдвинуть его на 4 разряда, компилятор генерирует цикл, в котором честно сдвигает все 4 байта на 28 разрядов. Это у него занимает 28*(4+1) + 2 = 142 цикла вместо 3 (mov, swap, andi), которые должны быть. А ведь это выполняется в цикле 9 раз, итого 1278 такта — дофига для такой мелочи. Приходится компилятору немного помогать.

Плавающая точка.

Для преобразования чисел с плавающей точкой в строку тоже можно придумать много вариантов, но я этого делать не буду. Приведу только один вариант и сравню его с sprintf.
Этот вариант будет, естественно, умножение на 10, потому, что чтоб вытащить цифры из мантиссы, её всё-равно придётся умножать. В методах умножения на 10 для целых и чисел с фиксированной точкой, начальное 64 битное умножение занимало заметную долю времени. Поэтому они оказывались несколько медленнее, чем некоторые другие, несмотря на то, что умножение на 10 само по себе быстрее, чем деление. В случае с плавающей точкой он должен оказаться безоговорочным лидером.
Для начала посмотрим на что способна sprintf, при выводе float-ов.
Для AVR sprintf работает в среднем за 2000 такта, на STM32 — 3950. Не стоит ругать sprintf на STM32 — помним, что там преобразуется double, а не float.
При этом sprintf с ключиком %g округляет результат до заданной точности, удаляет незначащие нули и выводит результат в наиболее подходящем виде: в обычном 123.45, или научном 1.2345e+2.
Задача состоит в том, чтоб получить вывод идентичный sprintf, но гораздо быстрее.
Разделим задачу на две части:
1 — извлечение из float-а значащих цифр и десятичной экспоненты;
2 — форматирование результата в нужном виде.
Для первой части нужно:
— распаковать float, вытащить из него мантиссу, двоичную экспоненту и знак;
— обработать особые случаи: ±0, ±inf, nan.
— получить десятичную экспоненту;
— получить множитель 2 в степени экспоненты, делённая на степень 10, такую, чтоб не было переполнения;
— умножить мантиссу на этот множитель;
— извлечь требуемое количество значащих цифр + одну для округления;
— округлить полученные цифры;
— удалить незначащие нули.

Извлечение цифр и десятичной экспоненты выделим в отдельную функцию:

presc — это необходимое количество значащих цифр.
Теперь распакуем float:

Финт ушами со сгвигим экспоненты на 16 бит, а потом еще на 7, опять-же чтоб задобрить компилятор.
Теперь извлечём знак:

Первый символ в выходном буфере — знак + или -.
Обрабатываем ±0, ±inf, nan:

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

Вычислить требуемый множитель за приемлемое время не получится, можно конечно, сдвигать влево-вправо, умножать-делить на 10 константу в цикле в зависимости от экспоненты, но это очень медленно. Поэтому множитель нужно брать из таблицы. Но 256 32-х битный чисел — этож целый килобайт, слишком жирно. Соседние элементы этой таблицы будут отличаться на степень 2, следовательно результат умножения для соседних элементов можно получить сдвигая его в нужную сторону на количество бит, равное расстоянию между этими элементами. Поскольку мантисса 24-х разрядная, то сдвигать её можно на 8 разрядов без потери точности. Это значит, что в таблице можно оставить только каждый 8 элемент, 32 элемента — уже приемлемо. Чтоб сохранить точность, сначала будем сдвигать мантиссу на 8 бит влево, потом умножать, потом сдвигать влево на требуемое количество бит. Так удаётся сохранить все 24 бита точности не выходя за 32-х битную арифметику (кроме умножения, конечно).
Десятичную экспоненту несложно вычислить из двоичной, они связаны линейной зависимостью. Сложность только в том, чтоб подобрать масштабирующие и сдвигающие коэффициенты, при которых будет верный результат во всём диапазоне входных значений, и не вылезти при этом за пределы 16-ти битной арифметики.

Теперь удалим ведущие нули:

Удаление завершающих нулей.

В итоге в буфере знак и значащие цифры, десятичная экспонента в возвращаемом из функции значении.
Теперь дело за форматированием:

Получилось достаточно многословно, но результат того стоил. На AVR в среднем 759 тактов, на STM32 — 425. Для сравнения в avr-libc есть функция dtostre, которая использует для преобразования тот-же движок, что и sprintf, но выводит всегда в экспоненциальном формате и не удаляет незначащие нули. Её среднее время в этом тесте — 1215 тактов. И это при том, что преобразование там реализовано на ассемблере.

Результат тестов с фиксированной точкой для AVR:

Результат тестов с фиксированной точкой для STM32:

Результат тестов с плавающей точкой для AVR:

Результат тестов с плавающей точкой для STM32:

Все тесты проведены на компиляторах avr-gcc и arm-gcc соответственно, с оптимизацией -O3.

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

Но как заставить эту точку плавать? Этим занимается процессор, то есть аппаратная часть компьютера. Во многих современных процессорах даже есть специальные команды для операций над числами с плавающей точкой (но об этом позже). В большинстве компьютеров используются именно числа с плавающей точкой (а не с фиксированной), потому что это позволяет экономить память и получать большую точность.

  1. Перевести число из Р-ичной системы в двоичную
  2. Представить двоичное число в нормализованной экспоненциальной форме
  3. Рассчитать смещённый порядок числа
  4. Разместить знак, порядок и мантиссу в соответствующие разряды

Знак Порядок Мантисса
0 10000 0010001100

Как видите, в мантиссе у нас младшие два разряда – это нули. Эти разряды нами не используются, но при желании мы бы могли их использовать и тем самым повысить точность.

А теперь давайте спустимся с небес на землю. Все приведённые нами примеры являются упрощёнными. В реальных машинах обычно для чисел с плавающей точкой используются числа с большим количеством разрядов. Но реальные числа мы здесь рассматривать не будем, тем более что представление данных может отличаться в зависимости от процессора. Для первого знакомства информации достаточно. Возможно, что эту тему я расширю в будущих изданиях книги. А пока, если хотите знать больше – изучайте стандарт IEEE 754, который реализован во всех х86-совместимых процессорах. Ну а нам пора переходить непосредственно к Ассемблеру…

На этом уроке мы рассмотрим типы данных с плавающей точкой в языке С++, их точность и диапазон. Выясним, что такое экспоненциальная запись и как она используется, а также рассмотрим ошибки округления и дадим определения для nan и inf .

Типы данных с плавающей точкой

Есть три типа данных с плавающей точкой: float, double и long double. Язык C++ определяет только их минимальный размер (как и с целочисленными типами). Типы данных с плавающей точкой всегда являются signed (т.е. могут хранить как положительные, так и отрицательные числа).

Тип Минимальный размер Типичный размер
Тип данных с плавающей точкой float 4 байта 4 байта
double 8 байт 8 байт
long double 8 байт 8, 12 или 16 байт

Объявление переменных разных типов данных с плавающей точкой:

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

Обратите внимание, литералы типа с плавающей точкой по умолчанию относятся к типу double. f в конце числа означает тип float.

Экспоненциальная запись

Обычно, в экспоненциальной записи, в целой части находится только одна цифра, все остальные пишутся после разделительной точки (в дробной части).

Рассмотрим массу Земли. В десятичной системе счисления она представлена как 5973600000000000000000000 кг . Согласитесь, очень большое число (даже слишком большое, чтобы поместиться в целочисленную переменную размером 8 байт). Это число даже трудно читать (там 19 или 20 нулей?). Но используя экспоненциальную запись, массу Земли можно представить, как 5.9736 x 10 24 кг (что гораздо легче воспринимается, согласитесь). Еще одним преимуществом экспоненциальной записи является сравнение двух очень больших или очень маленьких чисел — для этого достаточно просто сравнить их экспоненты.

В языке C++ буква е / Е означает, что число 10 нужно возвести в степень, которая следует за этой буквой. Например, 1.2 x 10 4 эквивалентно 1.2e4 , значение 5.9736 x 10 24 еще можно записать как 5.9736e24 .

Для чисел меньше единицы экспонент может быть отрицательным. Например, 5e-2 эквивалентно 5 * 10 -2 , что, в свою очередь, означает 5 / 10 2 или 0.05 . Масса электрона равна 9.1093822e-31 кг .

На практике экспоненциальная запись может использоваться в операциях присваивания следующим образом:

Мантисса дробная часть десятичного логарифма (то есть логарифм по основанию 10), которые представляют цифры данного числа, но не его порядок величины. Например, мантисса log1020?1.3010 и log10200?2.3010 равна 0.3010.

далее, как преобразовать мантиссу и экспоненту в десятичные числа?

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

Таким образом, что такое мантисса с примером? Мантисса - это часть числа, стоящая после десятичной точки. Пример мантиссы: 234 в числе 1101.234. (математика, вычисления, запрещено) Значение; та часть числа с плавающей запятой или числа в экспоненциальном представлении, которая содержит его значащие цифры.

Что такое представление чисел с плавающей запятой?

Представление с плавающей точкой похоже по концепции на научную запись. Логически, число с плавающей точкой состоит из: Знаковая (то есть положительная или отрицательная) строка цифр заданной длины в заданной базе (или основание). Эта строка цифр называется мантиссой или коэффициентом.

Что такое формула Antilog?

Как читать мантиссу?

В десятичном формате очень большие числа могут отображаться с мантиссой и показателем степени. т.е. 0.12 * 10? Здесь 0.12 - мантисса, а 10? - показатель степени. мантисса содержит основные цифры, а показатель степени определяет, где должна быть размещена десятичная точка. Тот же метод можно использовать для двоичных чисел.

Может ли мантисса быть отрицательной?

Мантисса с дополнением до двух допускает положительные и отрицательные значения для хранения.

Как решить мантиссу?

Целая часть десятичного логарифма называется характеристикой, а неотрицательная десятичная часть - мантиссой. Предположим, log 39.2 = 1.5933, тогда 1 - характеристика, а 5933 - мантисса логарифма. Если log. 009423 = - 3 +.

Какая польза от мантиссы?

мантисса содержит основные цифры, а показатель степени определяет, где должна быть размещена десятичная точка. Эту же технику можно использовать для двоичные числа. Например, два байта можно разделить так, чтобы 10 бит использовались для мантиссы, а оставшиеся 6 - для экспоненты.

Какова функция мантиссы?

Возвращает мантиссу и показатель степени входящего числового значения, так что число = мантисса * 2 ^ экспонента. Если число равно 0, мантисса и показатель степени равны 0. В противном случае абсолютное значение мантиссы больше или равно 1 и меньше 2, а значение показателя степени является целым числом.

Что такое представление с фиксированной точкой. Объясните на примерах?

Обозначение с фиксированной точкой имеет подразумеваемая двоичная точка между целым и дробным битами, аналог десятичной точки между целыми и дробными цифрами обычного десятичного числа. Например, на рис. 5.22 (a) показано число с фиксированной точкой с четырьмя целыми и четырьмя дробными битами.

Почему это называется с плавающей точкой?

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

Что такое двойное против поплавка?

Double более точен, чем float, и может хранить 64 бита, удвоенное количество битов, которое может хранить плавающий. Double более точен, и для хранения больших чисел мы предпочитаем double float. … Если нам не нужна точность до 15 или 16 десятичных знаков, в большинстве приложений мы можем придерживаться float, так как double дороже.

Может ли 107 быть мантиссой?

Когда вы рассматриваете десятичное число 12.34 * 107, это также можно рассматривать как 0.1234 * 109, где 0.1234 - мантисса неподвижной точки. Другая часть представляет значение экспоненты и указывает, что фактическое положение двоичной точки находится на 9 позиций справа (слева) от указанной двоичной точки в дроби.

Что такое журнал 1?

Чаще всего используются логарифмические функции с основанием 10 и e. Функция общего логарифма - функция логарифма с основанием 10 известна как функция общего логарифма.

Таблицы журналов с 1 по 10 для Log Base 10.

Десятичный логарифм числа (log10 x) Значения журнала
Журнал 1
0
Журнал 2 0.3010
Журнал 3 0.4771
Журнал 4 0.6020

Как рассчитать антилог на калькуляторе?

  1. Что такое Антилог? Antilog - это более короткая версия Anti-Logarithms. …
  2. Как найти Антилог в простом калькуляторе? Просто умножьте 10 в степень числа. …
  3. Как рассчитать антилог отрицательных чисел?

Как решить антилог?

Чтобы вычислить антилогарифм числа y, вы должен возвести логарифм с основанием b (обычно 10, иногда константа e) в степень, которая будет генерировать число y. Где x - показатель степени, а y - значение антилогарифма. Например, если мы возьмем это уравнение, log (5) = x, его антилогарифм будет 10x = 5.

Почему мы прибавляем 127 к показателю степени?

Поле экспоненты должно представлять как положительные, так и отрицательные показатели. А смещение добавляется к фактической экспоненте чтобы получить сохраненную экспоненту. Для чисел с плавающей запятой одинарной точности IEEE это значение равно 127. Таким образом, показатель степени, равный нулю, означает, что 127 хранится в поле показателя степени.

Мантисса отрицательная или положительная?

мантисса 1425, 142.5 и 14.25. При использовании этого определения также отмечается, что мантисса равна всегда неотрицательное десятичное число.

Как преобразовать отрицательную мантиссу в положительную мантиссу?

Как узнать, отрицательно ли число с плавающей запятой?

Отрицательные значения представляют отрицательные числа. Показатель, который говорит где десятичная (или двоичная) точка размещается относительно начала мантиссы. Отрицательные показатели представляют собой очень маленькие числа (т. Е. Близкие к нулю).

Что такое формула antilog?

Каковы 4 закона логарифмов?


Правила логарифмов

  • Правило 1: Правило продукта. …
  • Правило 2: Правило частной доли. …
  • Правило 3: Правило силы. …
  • Правило 4: Правило нуля. …
  • Правило 5: Правило идентичности. …
  • Правило 6: Логарифм правила экспоненты (логарифм от основания до правила мощности)…
  • Правило 7: экспонента логарифмического правила (основа правила логарифмической мощности)

Где мантисса в Python?

Python | frexp () Функция

Он возвращает мантиссу и экспоненту как пару (m, e) числа данное значение x, где мантисса m - число с плавающей запятой, а показатель степени e - целочисленное значение. m - это число с плавающей запятой, а e - целое число, такое что x == m * 2 ** e точно. Если x равен нулю, возвращает (0.0, 0), в противном случае 0.5 Математики, Анализ эффективности, наука

Читайте также: