Почему нет байтов с 2 байтами и реализация уже существует?

Предполагая, что я действительно нажал на память и хочу меньший диапазон (аналогично short vs int). Шейдерные языки уже поддерживают half для плавающей запятой с половиной точности (не просто конвертировать назад и вперед для значения между -1 и 1, то есть возвращать float следующим образом: shortComingIn / maxRangeOfShort). Есть ли реализация, которая уже существует для 2 байтов float?

Мне также интересно узнать какие-либо (исторические?) причины, почему нет 2 байтового поплавка.

Ответ 2

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

uint16_t cash = 50000;
std::cout << "Cash: $" << (cash / 100) << "." << ((cash % 100) < 10 ? "0" : "") << (cash % 100) << std::endl;

Это, конечно, только вариант, если вы можете заранее определить положение десятичной точки. Но если вы можете, всегда предпочитайте это, потому что это также ускоряет все вычисления!

ргдс, кира :-)

Ответ 4

Чтобы перейти немного дальше, чем Kiralein при переключении на целые числа, мы могли бы определить диапазон и разрешить целочисленные значения short, чтобы представлять равные деления по диапазону, с некоторой симметрией, если оседлать ноль:

short mappedval = (short)(val/range);

Различия между этими целочисленными версиями и использованием float с половинной точностью:

  • Целые числа равномерно распределены по диапазону, тогда как поплавки более плотно упакованы вблизи нуля
  • Использование целых чисел будет использовать целочисленную математику в ЦП, а не с плавающей запятой. Это часто бывает быстрее, потому что операции с целыми числами проще. Сказав, что отображение значений в асимметричный диапазон потребует дополнительных дополнений и т.д. Для получения значения в конце.
  • Абсолютная точность потерь более предсказуема; вы знаете ошибку в каждом значении, так что общая потеря может быть рассчитана заранее, учитывая диапазон. И наоборот, относительная ошибка более предсказуема с использованием плавающей запятой.
  • Может быть небольшой выбор операций, которые вы можете сделать, используя пары значений, особенно побитовые операции, путем упаковки двух шорт в int. Это может сократить вдвое количество циклов (или более, если короткие операции включают в себя преобразование в int) и поддерживает 32-битную ширину. Это всего лишь разбавленная версия бит-нарезки, в которой 32 бита действуют параллельно, что используется в криптографии.

Ответ 5

Вероятно, существует множество типов в разных реализациях. Поплавковый эквивалент stdint.h кажется хорошей идеей. Назовите (псевдоним?) Типы по размерам. (float16_t?) Число с плавающей запятой размером 4 байта только сейчас, но, вероятно, не станет меньше. Такие термины, как половина и длинный, в основном со временем теряют смысл С 128 или 256 битными компьютерами они могут означать что угодно.

Я работаю с изображениями (1 + 1 + 1 байт/пиксель) и хочу выразить значение каждого пикселя относительно среднего. Так что с плавающей точкой или тщательно фиксированной точкой, но не в 4 раза больше, чем необработанные данные, пожалуйста. 16-битный плавающий звучит о праве.

Этот GCC 7.3 не знает "половину", может быть, в контексте C++.

Ответ 6

TL; DR: 16-разрядные числа с плавающей запятой существуют, и существуют различные программные и аппаратные реализации

В настоящее время существует 2 распространенных стандартных 16-разрядных формата с плавающей запятой: IEEE-754 binary16 и Google bfloat16. Поскольку они стандартизированы, очевидно, если кто-то, кто знает спецификацию, может написать реализацию. Некоторые примеры:

Или, если вы не хотите их использовать, вы можете также разработать другой 16-битный формат с плавающей запятой и реализовать его


2-байтовые операции с плавающей запятой обычно не используются, потому что даже точности с плавающей запятой недостаточно для обычных операций, и по умолчанию всегда следует использовать double если вы не ограничены пропускной способностью или размером кэша. Литералы с плавающей точкой также double при использовании без суффикса в C и C-подобных языках. Увидеть

Однако менее 32-битные числа с плавающей точкой существуют. Они в основном используются в целях хранения, как в графике, когда 96 бит на пиксель (32 бита на канал * 3 канала) слишком потрачены впустую, и будут преобразованы в обычный 32-битный float для вычислений (за исключением некоторых специальных аппаратных средств)). В OpenGL существуют различные 10, 11, 14-битные типы с плавающей точкой. Во многих форматах HDR для каждого канала используется 16-разрядное число с плавающей запятой, а Direct3D 9.0, а также некоторые графические процессоры, такие как Radeon R300 и R420, имеют 24-разрядный формат с плавающей запятой. 24-разрядное плавание также поддерживается компиляторами в некоторых 8-разрядных микроконтроллерах, таких как PIC, где поддержка 32-разрядного плавания слишком дорога. 8-битные или более узкие типы с плавающей точкой менее полезны, но из-за своей простоты их часто преподают в учебных программах по информатике. Кроме того, небольшой код с плавающей запятой также используется в кодировании команд ARM для небольших операций с плавающей запятой.

В ревизию IEEE 754-2008 официально добавлен 16-битный формат с плавающей запятой, AKA binary16 или с половинной точностью, с 5-битным показателем степени и 11-битной мантиссой

Некоторые компиляторы поддерживают двоичный код IEEE-754, но в основном для преобразования или векторизованных операций, а не для вычислений (потому что они недостаточно точны). Например, ARM toolchain имеет __fp16 который может быть выбран из двух вариантов: IEEE и альтернативный в зависимости от того, хотите ли вы больше представлений диапазона или NaN/inf. GCC и Clang также поддерживают __fp16 вместе со стандартным именем _Float16. См. Как включить тип __fp16 в gcc для x86_64

В последнее время из-за роста ИИ стал широко распространенным другой формат, названный bfloat16 (формат с плавающей запятой для мозга), который представляет собой простое усечение старших 16 бит двоичного кода IEEE-75432.

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

  • float32: 242 = 576 (100%)
  • float16: 112 = 121 (21%)
  • bfloat16: 82 = 64 (11%)

Многие компиляторы, такие как GCC и ICC, теперь также получили возможность поддерживать bfloat16.

Больше информации о bfloat16:

Ответ 7

Если ваш процессор поддерживает F16C, то вы можете довольно быстро запустить и запустить что-нибудь, например:

// needs to be compiled with -mf16c enabled
#include <immintrin.h>
#include <cstdint>

struct float16
{
private:
  uint16_t _value;
public:

  inline float16() : _value(0) {}
  inline float16(const float16&) = default;
  inline float16(float16&&) = default;
  inline float16(const float f) : _value(_cvtss_sh(f, _MM_FROUND_CUR_DIRECTION)) {}

  inline float16& operator = (const float16&) = default;
  inline float16& operator = (float16&&) = default;
  inline float16& operator = (const float f) { _value = _cvtss_sh(f, _MM_FROUND_CUR_DIRECTION); return *this; }

  inline operator float () const 
    { return _cvtsh_ss(_value); }

  inline friend std::istream& operator >> (std::istream& input, float16& h) 
  { 
    float f = 0;
    input >> f;
    h._value = _cvtss_sh(f, _MM_FROUND_CUR_DIRECTION);
    return input;
  }
};

Математика по-прежнему выполняется с использованием 32-разрядных чисел (расширения F16C обеспечивают преобразование только между 16/32-разрядными числами - не существует инструкций для вычисления арифметики с 16-разрядными числами).