为什么没有2字节浮点数,实现是否已经存在?
Why is there no 2-byte float and does an implementation already exist?
假设我真的很紧张,想要一个更小的范围(类似于short
vs int
)。Shader语言已经为具有一半精度的浮点类型支持half
(不仅仅是来回转换值在-1和1之间,也就是说,返回像这样的浮点数:shortComingIn / maxRangeOfShort
)。是否有一个已经存在的2字节浮点数的实现?
我也有兴趣知道为什么没有2字节浮点数的任何(历史?)原因。
TL;DR: 16位浮点数确实存在,并且有各种软件和硬件实现
目前有两种通用的标准16位浮点格式:IEEE-754 binary16和Google的bfloat16。由于它们是标准化的,显然任何了解规范的人都可以编写实现。一些例子:
- https://github.com/ramenhut/half
- https://github.com/minhhn2910/cuda-half2
- https://github.com/tianshilei1992/half_precision
- https://github.com/acgessler/half_float
或者如果你不想使用它们,你也可以设计一个不同的16位浮点格式并实现它
通常不使用
2字节浮点数,因为即使浮点数的精度也不足以用于正常操作和double
,除非您受到带宽或缓存大小的限制,否则默认情况下应该始终使用。当在C和类C语言中不带后缀时,浮点字面值也是double
。看到
- 为什么首选double而不是float?
- 我应该使用double还是float?
- 什么时候使用float,什么时候使用double
但是小于32位的浮点数确实存在。它们主要用于存储目的,例如在图形中,每像素96位(每通道32位* 3通道)太浪费了,并且将被转换为正常的32位浮点数进行计算(除非在某些特殊硬件上)。OpenGL中存在各种10、11、14位浮点类型。许多HDR格式对每个通道使用16位浮点数,Direct3D 9.0以及一些gpu(如Radeon R300和R420)具有24位浮点数格式。一些8位微控制器(如PIC)的编译器也支持24位浮点数,因为支持32位浮点数的成本太高。8位或更窄的浮点类型不太有用,但由于它们的简单性,它们经常在计算机科学课程中教授。此外,在ARM的指令编码中还使用了一个小浮点数来处理小浮点数。
IEEE 754-2008修订正式增加了16位浮点格式,又名binary16或半精度,具有5位指数和11位尾号
一些编译器支持IEEE-754 binary16,但主要用于转换或向量化操作,而不支持计算(因为它们不够精确)。例如,ARM的工具链有__fp16
,它可以在两个变体之间进行选择:IEEE和alternative,这取决于你是否想要更多的范围或NaN/inf表示。GCC和Clang也支持__fp16
以及标准化名称_Float16
。参见如何在gcc上为x86_64启用__fp16类型
最近由于AI的兴起,另一种称为bfloat16(大脑浮点格式)的格式简单截断IEEE-754 binary32的前16位变得普遍
减少尾数背后的动机来自Google的实验,该实验表明,只要减少尾数,仍然可以表示接近于零的微小值,作为训练过程中微小差异总和的一部分。更小的尾数带来了许多其他优点,如减少乘法器功率和物理硅面积。
- float32: 242=576 (100%)
- float16: 112=121 (21%)
- bfloat16: 82=64 (11%)
许多编译器如GCC和ICC现在也获得了支持bfloat16的能力
bfloat16的更多信息:
- bfloat16 -硬件数字定义
- 使用bfloat16与TensorFlow模型
- 什么是tf。截断的16位浮点数?
在bfloat16不够的情况下还有一个新的19位类型叫做TensorFloat
回复:实现:有人显然已经为C编写了half
,这将(当然)在c++中工作:https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/cellperformance-snippets/half.c
Re:为什么float
是四个字节:可能是因为低于这个,它们的精度是如此有限。在IEEE-754中,"half"&;只有11位有效精度,产生大约3.311个十进制数字的精度(相比之下,24位的单位产生6到9个十进制数字的精度,53位的双位产生15到17个十进制数字的精度)。
如果内存不足,是否考虑过放弃float的概念?浮点数仅仅为了保存小数点的位置就占用了很多位。你可以解决这个问题,如果你知道在哪里需要小数点,比如你想保存一个美元值,你可以保存为美分:
uint16_t cash = 50000;
std::cout << "Cash: $" << (cash / 100) << "." << ((cash % 100) < 10 ? "0" : "") << (cash % 100) << std::endl;
当然,只有当您可以预先确定小数点的位置时,这才是一种选择。但如果可以的话,总是更喜欢它,因为这也加快了所有的计算速度!
是IEEE 754的16位浮点数标准。
这是一种新的格式,在2008年基于2002年发布的GPU进行了标准化。
要比Kiralein更进一步,我们可以定义一个范围,并允许a short的整数值在该范围内表示相等的除数,如果跨越零,则具有一定的对称性:
short mappedval = (short)(val/range);
这些整数版本和使用半精度浮点数的差异:
- 整数在范围内间隔相等,而浮点数在零附近更密集地排列
- 使用整数将在CPU中使用整数数学而不是浮点数。这通常更快,因为整数操作更简单。话虽如此,将值映射到不对称范围将需要额外的添加等,以便在最后检索值。
- 绝对精度损失更可预测;您知道每个值的误差,因此在给定范围内,可以提前计算出总损失。相反,使用浮点数更容易预测相对误差。
- 可能有一小部分操作可以使用值对,特别是位操作,通过将两个short打包到int中。这可以将所需的循环次数减半(如果短操作涉及转换为int,则可以减少更多),并保持32位的宽度。这只是位切片的稀释版本,其中并行处理32位,用于加密。
如果你的CPU支持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位浮点数计算算术的指令)。
在不同的实现中可能有多种类型。stint .h的float等效物似乎是个好主意。根据类型的大小调用(别名?)( float16_t
?)一个4字节的浮点数只是现在,但它可能不会变得更小。像half和long这样的术语随着时间的推移变得毫无意义。对于128位或256位的计算机,它们可能意味着任何东西。
我正在处理图像(1+1+1字节/像素),我想表达每个像素相对于平均值的值。所以是浮点数还是谨慎的定点,但不要是原始数据的4倍。16位浮点数听起来很合适。
这个GCC 7.3不知道"half",可能在c++上下文中。
2字节浮点数在clang C编译器中是可用的,数据类型表示为__fp16
不同的编译器现在支持三种不同的半精度格式:
- __fp16主要用作存储格式。一旦你对它进行计算,它就会被提升为浮动。__fp16上的计算将给出浮点结果。__fp16具有5位指数和10位尾数。
- _Float16与__fp16相同,但用作交换和算术格式。对_Float16的计算将得到一个_Float16的结果。
- __bf16是精度较低的存储格式。它有8位指数和7位尾数。
这三种类型都被ARM架构的编译器支持,现在也被x86处理器的编译器支持。AVX512_FP16指令集扩展将由英特尔即将推出的Golden Cove处理器支持,并由最新的Clang、Gnu和英特尔编译器支持。在支持AVX512_FP16的编译器上,_Float16的向量定义为__m128h、__m256h和__m512h。
引用:https://developer.arm.com/documentation/100067/0612/Other-Compiler-specific-Features/Half-precision-floating-point-data-types
https://clang.llvm.org/docs/LanguageExtensions.html half-precision-floating-point
- 这个极客对极客的trie实现是否存在内存泄漏问题
- 如何检查QList中是否存在值
- 根据某个函数是否存在启用模板
- 地图计数确实很重要,或者只是检查是否存在
- C++中是否存在 std::conditional 的懒惰等价物?
- 无论如何,我可以确定构造函数是否存在吗?
- COM :是否可以查看是否存在对我的某个 COM 对象的进程外引用?我可以释放它吗?
- 堆分配的对象是否存在永不为空的唯一所有者?
- 扩展类中的可选 vir 函数,测试它在运行时是否存在
- 模板化检查是否存在带有参数列表的类成员函数?
- 是否存在包含负号的isdigit函数(过载)
- 如何巧妙地编写两个函数——一个用于检查是否存在解决方案,另一个用于获取所有解决方案
- 检查编译时是否存在静态函数
- 向量是否存在行主要形式?
- 检查 TinyXML 中的元素是否存在
- 检测是否存在具有 C++17 的类成员
- 虚拟继承中是否存在多重继承?
- 我遇到了这个代码片段,不明白. 它递归检查 C++ 字符串中是否存在大写字符
- std::weak_ptr 和相应的 std::shared_ptr 之间是否存在数据竞争?
- 是否存在用于 C 或 C++ 中常见数学运算(例如最小值、最大值和平均值)的可导入库?