假设我内存十分紧张,想要一个更小的范围(类似于short
和int
之间的区别)。着色器语言已经支持了half
作为浮点类型,并且可以达到一半的精度(而不仅仅是将值转换回和介于-1和1之间的值,即像这样返回一个浮点数:shortComingIn / maxRangeOfShort
)。是否已经有存在2字节浮点数的实现?
我还想知道任何(历史上的?)没有2字节浮点数的原因。
假设我内存十分紧张,想要一个更小的范围(类似于short
和int
之间的区别)。着色器语言已经支持了half
作为浮点类型,并且可以达到一半的精度(而不仅仅是将值转换回和介于-1和1之间的值,即像这样返回一个浮点数:shortComingIn / maxRangeOfShort
)。是否已经有存在2字节浮点数的实现?
我还想知道任何(历史上的?)没有2字节浮点数的原因。
目前有两种常见的16位浮点数标准格式:IEEE-754二进制16和Google的bfloat16。由于它们是标准化的,任何了解规范的人都可以编写实现。以下是一些示例:
或者,如果你不想使用它们,你也可以设计一个不同的16位浮点格式并实现它。
通常不使用2字节浮点数,因为即使是float的精度也不足以进行正常操作,默认情况下应始终使用double
,除非受到带宽或缓存大小的限制。在C和类似的语言中,使用浮点字面量时,如果没有后缀,则也是double
。请参阅
IEEE 754-2008修订版正式添加了一种16位浮点数格式,即二进制16或半精度,具有5位指数和11位尾数。
一些编译器支持IEEE-754二进制16,但主要用于转换或矢量化操作,而不是计算(因为它们不够精确)。例如,ARM的工具链具有__fp16
,可以在2个变体之间选择:IEEE和替代,具体取决于您是否需要更大的范围或NaN / inf表示。GCC和Clang也支持__fp16
以及标准名称_Float16
。请参见如何在x86_64上启用__fp16类型的gcc。
在bfloat16不足的情况下,还出现了一种新的19位类型,称为TensorFloat。
关于实现:据说有人为C语言写了half
,当然可以在C++中使用:https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/cellperformance-snippets/half.c
关于为什么float
是四个字节:可能是因为在此之下,它们的精度非常有限。在IEEE-754标准中,“half”只有11位有效数字精度,产生约3.311个十进制数字的精度(而在single中有24位,产生6到9个十进制数字的精度,在double中有53位,产生15到17个十进制数字的精度)。
如果你的内存不足,你是否考虑放弃浮点数的概念?浮点数会使用大量比特来保存小数点的位置。如果你知道需要小数点的位置,例如你想要保存一个美元值,你可以将它保存为分:
uint16_t cash = 50000;
std::cout << "Cash: $" << (cash / 100) << "." << ((cash % 100) < 10 ? "0" : "") << (cash % 100) << std::endl;
当然,这只是一个选项,前提是您能够预先确定小数点的位置。但是如果可以,始终选择它,因为这也加快了所有计算的速度!
std::cout << "现金:$" << (cash / 100) << "." << std::setfill('0') << std::setw(2) << (cash % 100) << std::endl;
- Et7f3XIV如果要比Kiralein更进一步地切换到整数,我们可以定义一个范围,并允许short类型的整数值表示范围内相等的分段,如果跨越零点,则需要一些对称性:
short mappedval = (short)(val/range);
如果你的CPU支持F16C,那么你可以通过以下方式快速启动:
注意:本文中"Original Answer"翻译成 "最初的回答"
// 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位浮点数进行算术运算)。
最初的回答
immintrin.h
的情况下完成。请参考此答案:https://dev59.com/P7Lma4cB1Zd3GeqPeKht#64493446 - wolfram77float16_t
?)。现在一个float是4个字节,但它可能不会变得更小。像half和long这样的术语随着时间的推移大多变得毫无意义。对于128位或256位的计算机,它们可以意味着任何东西。if(128>=64)std::cout<<"True\n"; else std::cout<<"False\n";
ENIAC在硬件上是十进制的,可以计算10或20位十进制数字。(这比40位和80位二进制好一点);EDVAC使用44位字长;SWAC使用37位字长,具有单精度或双精度(74位);EDSAC使用34位,使用两个17位字;曼彻斯特Mark 1使用40位数字和20位指令;MEG / Mercury浮点单位使用40位,30位尾数和10位指数。 - Max Power在clang C编译器中,有2字节浮点数可用,该数据类型表示为__fp16
。
所有三种类型都受到ARM架构编译器的支持,现在也受到x86处理器编译器的支持。AVX512_FP16指令集扩展将由英特尔即将推出的Golden Cove处理器支持,并且最新的Clang、Gnu和Intel编译器也支持它。在支持AVX512_FP16的编译器上,_Float16的向量被定义为__m128h、__m256h和__m512h。
参考资料:
https://clang.llvm.org/docs/LanguageExtensions.html#half-precision-floating-point
half
C++ 库 http://half.sourceforge.net/ - KindDragon