Subject: bug#68871: Can't use od to print half-precision floats

From: Pádraig Brady <[email-masked]>
Date: Thu, 1 Feb 2024 18:05:10 +0000

On 01/02/2024 12:43, Rozenberg, Eyal (Consultant) wrote:
> If I try to use the od utility to print half-precision (FP16) floating-point values, I get:
> 
> $ od -t f2 myfloats.bin
> od: invalid type string 'f2';
> this system doesn't provide a 2-byte floating point type
> 
> I'm not exactly sure what "this system" means, but that should work and print out my floats.
> 
> Eyal
> 
> PS - This is my first bug-coreutils post, please be gentle.

I just had a read of these:
https://gcc.gnu.org/onlinedocs/gcc/Half-Precision.html
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0192r4.html

On the face of it it seems that various 16 bit floating point formats have consolidated,
and we could add support for _Float16 in od.
I see gnulib already enables __STDC_WANT_IEC_60559_TYPES_EXT__
so we should be able to do something like the attached.

This is just off the top of my head, and I haven't tested
or thought about this much at all.
Any testing your could do would be appreciated.

thanks,
Pádraig

commit 82fc25ab7f34a8198a8f435c1396801d30e8c6b6
Author: Pádraig Brady <[email-masked]>
Date:   Thu Feb 1 17:59:51 2024 +0000

    od: support half precision floating point
    
    * src/od.c: Suport -t f2 to print 16 bit floating point.
    * NEWS: Mention the new feature.
    Addresses https://bugs.gnu.org/68871

diff --git a/src/od.c b/src/od.c
index 75bed5e7d..2f03a729e 100644
--- a/src/od.c
+++ b/src/od.c
@@ -19,6 +19,7 @@
 #include <config.h>
 
 #include <ctype.h>
+#include <float.h>
 #include <stdio.h>
 #include <getopt.h>
 #include <sys/types.h>
@@ -49,6 +50,12 @@ typedef unsigned long long int unsigned_long_long_int;
 typedef unsigned long int unsigned_long_long_int;
 #endif
 
+#ifndef FLT16_MAX
+/* 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
+
 enum size_spec
   {
     NO_SIZE,
@@ -58,6 +65,7 @@ enum size_spec
     LONG,
     LONG_LONG,
     /* FIXME: add INTMAX support, too */
+    FLOAT_HALF,
     FLOAT_SINGLE,
     FLOAT_DOUBLE,
     FLOAT_LONG_DOUBLE,
@@ -156,6 +164,7 @@ static const int width_bytes[] =
   sizeof (int),
   sizeof (long int),
   sizeof (unsigned_long_long_int),
+  sizeof (_Float16),
   sizeof (float),
   sizeof (double),
   sizeof (long double)
@@ -477,6 +486,7 @@ 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_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)
@@ -824,6 +834,11 @@ decode_one_format (char const *s_orig, char const *s, char const **next,
 
         switch (size_spec)
           {
+          case FLOAT_HALF:
+            print_function = 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 +1613,9 @@ main (int argc, char **argv)
   for (i = 0; i <= MAX_FP_TYPE_SIZE; i++)
     fp_type_size[i] = NO_SIZE;
 
+#ifdef FLT16_MAX
+  fp_type_size[sizeof (_Float16)] = 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