From: Pádraig Brady Date: Thu, 1 Feb 2024 17:59:51 +0000 (+0000) Subject: od: support half precision floating point X-Git-Tag: v9.5~72 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=76604db7d2da0ceb4de4542ebf315b3d6415f972;p=thirdparty%2Fcoreutils.git od: support half precision floating point Rely on compiler support for _Float16 and __bf16 to support -fH and -fB formats respectively. I.e. IEEE 16 bit, and brain 16 bit floats respectively. Modern GCC and LLVM compilers support both types. clang-sect=half-precision-floating-point https://gcc.gnu.org/onlinedocs/gcc/Half-Precision.html https://clang.llvm.org/docs/LanguageExtensions.html#$clang-sect https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0192r4.html https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1467r9.html This was tested on: gcc 13, clang 17 x86 (Both types supported) gcc 7 aarch64 (Only -fH supported) gcc 13 ppc(be) (Neither supported. Both will be with GCC 14) * src/od.c: Support -tf2 or -tfH to print IEEE 16 bit floating point, or -tfB to print Brain 16 bit floating point. * configure.ac: Check for _Float16 and __bf16 types. * doc/coreutils.texi (od invocation): Mention the new -f types. * tests/od/od-float.sh: Add test cases. * NEWS: Mention the new feature. Addresses https://bugs.gnu.org/68871 --- diff --git a/NEWS b/NEWS index dc5d875ddc..5b5befd2cb 100644 --- a/NEWS +++ b/NEWS @@ -57,6 +57,9 @@ GNU coreutils NEWS -*- outline -*- chgrp now accepts the --from=OWNER:GROUP option to restrict changes to files with matching current OWNER and/or GROUP, as already supported by chown(1). + od now supports printing IEEE half precision floating point with -t fH, + or brain 16 bit floating point with -t fB, where supported by the compiler. + tail now supports following multiple processes, with repeated --pid options. ** Improvements diff --git a/configure.ac b/configure.ac index 48ab4ef533..64ff32a96f 100644 --- a/configure.ac +++ b/configure.ac @@ -522,6 +522,9 @@ CFLAGS=$ac_save_CFLAGS LDFLAGS=$ac_save_LDFLAGS ac_c_werror_flag=$cu_save_c_werror_flag +# Test compiler support for half precision floating point types (for od) +AC_CHECK_TYPES([_Float16, __bf16]) + ac_save_CFLAGS=$CFLAGS CFLAGS="-mavx -mpclmul $CFLAGS" AC_MSG_CHECKING([if pclmul intrinsic exists]) diff --git a/doc/coreutils.texi b/doc/coreutils.texi index d13e0f834d..d7cf5e08aa 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -2127,6 +2127,12 @@ long For floating point (@code{f}): @table @asis +@item B +@uref{https://en.wikipedia.org/wiki/Bfloat16_floating-point_format, +brain 16 bit float} +@item H +@uref{https://en.wikipedia.org/wiki/Half-precision_floating-point_format, +half precision float} @item F float @item D diff --git a/src/od.c b/src/od.c index 75bed5e7db..1e24893671 100644 --- a/src/od.c +++ b/src/od.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -49,6 +50,26 @@ typedef unsigned long long int unsigned_long_long_int; typedef unsigned long int unsigned_long_long_int; #endif +#if HAVE__FLOAT16 + /* Available since clang 6 (2018), and gcc 7 (2017). */ + typedef _Float16 float16; +#else +# define HAVE__FLOAT16 0 + /* This is just a place-holder to avoid a few '#if' directives. + In this case, the type isn't actually used. */ + typedef float float16; +#endif + +#if HAVE___BF16 + /* Available since clang 11 (2020), and gcc 13 (2023). */ + typedef __bf16 bfloat16; +#else +# define HAVE___BF16 0 + /* This is just a place-holder to avoid a few '#if' directives. + In this case, the type isn't actually used. */ + typedef float bfloat16; +#endif + enum size_spec { NO_SIZE, @@ -58,6 +79,7 @@ enum size_spec LONG, LONG_LONG, /* FIXME: add INTMAX support, too */ + FLOAT_HALF, FLOAT_SINGLE, FLOAT_DOUBLE, FLOAT_LONG_DOUBLE, @@ -71,6 +93,8 @@ enum output_format OCTAL, HEXADECIMAL, FLOATING_POINT, + HFLOATING_POINT, + BFLOATING_POINT, NAMED_CHARACTER, CHARACTER }; @@ -156,6 +180,11 @@ static const int width_bytes[] = sizeof (int), sizeof (long int), sizeof (unsigned_long_long_int), +#if HAVE___BF16 + sizeof (bfloat16), +#else + sizeof (float16), +#endif sizeof (float), sizeof (double), sizeof (long double) @@ -400,8 +429,9 @@ TYPE is made up of one or more of these specifications:\n\ \n\ SIZE is a number. For TYPE in [doux], SIZE may also be C for\n\ sizeof(char), S for sizeof(short), I for sizeof(int) or L for\n\ -sizeof(long). If TYPE is f, SIZE may also be F for sizeof(float), D\n\ -for sizeof(double) or L for sizeof(long double).\n\ +sizeof(long). If TYPE is f, SIZE may also be B for Brain 16 bit,\n\ +H for Half precision float, F for sizeof(float), D for sizeof(double),\n\ +or L for sizeof(long double).\n\ "), stdout); fputs (_("\ \n\ @@ -477,6 +507,8 @@ PRINT_TYPE (print_int, unsigned int) PRINT_TYPE (print_long, unsigned long int) PRINT_TYPE (print_long_long, unsigned_long_long_int) +PRINT_FLOATTYPE (print_bfloat, bfloat16, ftoastr, FLT_BUFSIZE_BOUND) +PRINT_FLOATTYPE (print_halffloat, float16, ftoastr, FLT_BUFSIZE_BOUND) PRINT_FLOATTYPE (print_float, float, ftoastr, FLT_BUFSIZE_BOUND) PRINT_FLOATTYPE (print_double, double, dtoastr, DBL_BUFSIZE_BOUND) PRINT_FLOATTYPE (print_long_double, long double, ldtoastr, LDBL_BUFSIZE_BOUND) @@ -773,6 +805,18 @@ decode_one_format (char const *s_orig, char const *s, char const **next, ++s; switch (*s) { + case 'B': + ++s; + fmt = BFLOATING_POINT; + size = sizeof (bfloat16); + break; + + case 'H': + ++s; + fmt = HFLOATING_POINT; + size = sizeof (float16); + break; + case 'F': ++s; size = sizeof (float); @@ -802,7 +846,10 @@ decode_one_format (char const *s_orig, char const *s, char const **next, else { if (size > MAX_FP_TYPE_SIZE - || fp_type_size[size] == NO_SIZE) + || fp_type_size[size] == NO_SIZE + || (! HAVE__FLOAT16 && HAVE___BF16 + && size == sizeof (bfloat16)) + ) { error (0, 0, _("invalid type string %s;\n" @@ -817,6 +864,15 @@ decode_one_format (char const *s_orig, char const *s, char const **next, } size_spec = fp_type_size[size]; + if ((! HAVE__FLOAT16 && fmt == HFLOATING_POINT) + || (! HAVE___BF16 && fmt == BFLOATING_POINT)) + { + error (0, 0, + _("this system doesn't provide a %s floating point type"), + quote (s_orig)); + return false; + } + { struct lconv const *locale = localeconv (); size_t decimal_point_len = @@ -824,6 +880,12 @@ decode_one_format (char const *s_orig, char const *s, char const **next, switch (size_spec) { + case FLOAT_HALF: + print_function = fmt == BFLOATING_POINT + ? print_bfloat : print_halffloat; + field_width = FLT_STRLEN_BOUND_L (decimal_point_len); + break; + case FLOAT_SINGLE: print_function = print_float; field_width = FLT_STRLEN_BOUND_L (decimal_point_len); @@ -1598,6 +1660,11 @@ main (int argc, char **argv) for (i = 0; i <= MAX_FP_TYPE_SIZE; i++) fp_type_size[i] = NO_SIZE; +#if HAVE__FLOAT16 + fp_type_size[sizeof (float16)] = FLOAT_HALF; +#elif HAVE___BF16 + fp_type_size[sizeof (bfloat16)] = FLOAT_HALF; +#endif fp_type_size[sizeof (float)] = FLOAT_SINGLE; /* The array entry for 'double' is filled in after that for 'long double' so that if they are the same size, we avoid any overhead of diff --git a/tests/od/od-float.sh b/tests/od/od-float.sh index 35a6d1249a..239b5f10e4 100755 --- a/tests/od/od-float.sh +++ b/tests/od/od-float.sh @@ -69,4 +69,25 @@ set x $(printf 00000000ff000000 | tr 0f '\000\377' | od -t fL) || fail=1 #*) fail=1;; #esac +# Check Half precision IEEE 16 bit float +if grep '^#define HAVE__FLOAT16 1' "$CONFIG_HEADER" >/dev/null; then + for fmt in '-tfH' '-tf2'; do + od_out=$(printf '\x3C\x00\x3C\x00' | od --endian=big -An $fmt | tr -d ' ') + test "$od_out" = '11' || fail=1 + done +else + echo "od: this system doesn't provide a 'fH' floating point type" > exp_err + returns_ 1 od -tfH /dev/null 2>err || fail=1 + compare exp_err err || fail=1 +fi +# Check Half precision Brain 16 bit float +if grep '^#define HAVE___BF16 1' "$CONFIG_HEADER" >/dev/null; then + od_out=$(printf '\x3F\x80\x3F\x80' | od --endian=big -An -tfB | tr -d ' ') + test "$od_out" = '11' || fail=1 +else + echo "od: this system doesn't provide a 'fB' floating point type" > exp_err + returns_ 1 od -tfB /dev/null 2>err || fail=1 + compare exp_err err || fail=1 +fi + Exit $fail