Оказывается, процессор Apple M2 поддерживает в хардвере тип данных BFloat16. Подробности есть в статье: "BFloat16 processing for Neural Networks on Armv8-A".
Проверить факт наличия BFloat16 на конкретном маке можно командой:
Алгоритм преобразования:
Проверить факт наличия BFloat16 на конкретном маке можно командой:
К примеру, команда BFCVT превращает число float32 в bfloat16: BFCVT--Floating-point-convert-from-single-precision-to-BFloat16-format.$ sysctl hw.optional.arm.FEAT_BF16
hw.optional.arm.FEAT_BF16: 1
Алгоритм преобразования:
// FPConvertBF()
// =============
// Converts a single-precision OP to BFloat16 value with using rounding mode of
// Round to Nearest Even when executed from AArch64 state and
// FPCR.AH == '1', otherwise rounding is controlled by FPCR/FPSCR.
bits(16) FPConvertBF(bits(32) op, FPCRType fpcr, FPRounding rounding)
bits(32) result; // BF16 value in top 16 bits
boolean altfp = HaveAltFP() && !UsingAArch32() && fpcr.AH == '1';
boolean fpexc = !altfp; // Generate no floating-point exceptions
if altfp then fpcr.<FIZ,FZ> = '11'; // Flush denormal input and output to zero
if altfp then rounding = FPRounding_TIEEVEN; // Use RNE rounding mode
// Unpack floating-point operand, with always flush-to-zero if fpcr.AH == '1'.
(fptype,sign,value) = FPUnpack(op, fpcr, fpexc);
if fptype == FPType_SNaN || fptype == FPType_QNaN then
if fpcr.DN == '1' then
result = FPDefaultNaN(fpcr);
else
result = FPConvertNaN(op);
if fptype == FPType_SNaN then
if fpexc then FPProcessException(FPExc_InvalidOp, fpcr);
elsif fptype == FPType_Infinity then
result = FPInfinity(sign);
elsif fptype == FPType_Zero then
result = FPZero(sign);
else
result = FPRoundCVBF(value, fpcr, rounding, fpexc);
// Returns correctly rounded BF16 value from top 16 bits
return result<31:16>;
// FPConvertBF()
// =============
// Converts a single-precision operand to BFloat16 value.
bits(16) FPConvertBF(bits(32) op, FPCRType fpcr)
return FPConvertBF(op, fpcr, FPRoundingMode(fpcr));В компиляторе Си имеются соответствующие встроенные тип и функции. Вот пример.
#include <stdio.h>
int main()
{
float f = 123.456;
__bf16 b;
b = __builtin_aarch64_bfcvtbf(f);
printf("From float32 to bfloat16: %.3f -> %#04hx\n", f, b);
f = __builtin_aarch64_bfcvtsf(b);
printf("From bfloat16 to float32: %#04hx -> %.3f\n", b, f);
}Компилируем, запускаем:
$ gcc-13 -O -march=armv8.6-a+bf16 bfdemo.c -o bfdemo
$ ./bfdemo
From float32 to bfloat16: 123.456 -> 0x42f7
From bfloat16 to float32: 0x42f7 -> 123.500