| 
									
										
										
										
											2019-09-13 12:12:17 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Common Float Helpers | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This contains a series of useful utility routines and a set of | 
					
						
							|  |  |  |  * floating point constants useful for exercising the edge cases in | 
					
						
							|  |  |  |  * floating point tests. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2024-02-27 14:43:07 +00:00
										 |  |  |  * Copyright (c) 2019, 2024 Linaro | 
					
						
							| 
									
										
										
										
											2019-09-13 12:12:17 +01:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2024-02-27 14:43:07 +00:00
										 |  |  |  * SPDX-License-Identifier: GPL-2.0-or-later | 
					
						
							| 
									
										
										
										
											2019-09-13 12:12:17 +01:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* we want additional float type definitions */ | 
					
						
							|  |  |  | #define __STDC_WANT_IEC_60559_BFP_EXT__
 | 
					
						
							|  |  |  | #define __STDC_WANT_IEC_60559_TYPES_EXT__
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define _GNU_SOURCE
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <inttypes.h>
 | 
					
						
							|  |  |  | #include <math.h>
 | 
					
						
							|  |  |  | #include <float.h>
 | 
					
						
							|  |  |  | #include <fenv.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-17 17:23:24 +01:00
										 |  |  | #include "../float_helpers.h"
 | 
					
						
							| 
									
										
										
										
											2019-09-13 12:12:17 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Half Precision Numbers | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Not yet well standardised so we return a plain uint16_t for now. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* no handy defines for these numbers */ | 
					
						
							|  |  |  | static uint16_t f16_numbers[] = { | 
					
						
							|  |  |  |     0xffff, /* -NaN / AHP -Max */ | 
					
						
							|  |  |  |     0xfcff, /* -NaN / AHP */ | 
					
						
							|  |  |  |     0xfc01, /* -NaN / AHP */ | 
					
						
							|  |  |  |     0xfc00, /* -Inf */ | 
					
						
							|  |  |  |     0xfbff, /* -Max */ | 
					
						
							|  |  |  |     0xc000, /* -2 */ | 
					
						
							|  |  |  |     0xbc00, /* -1 */ | 
					
						
							|  |  |  |     0x8001, /* -MIN subnormal */ | 
					
						
							|  |  |  |     0x8000, /* -0 */ | 
					
						
							|  |  |  |     0x0000, /* +0 */ | 
					
						
							|  |  |  |     0x0001, /* MIN subnormal */ | 
					
						
							|  |  |  |     0x3c00, /* 1 */ | 
					
						
							|  |  |  |     0x7bff, /* Max */ | 
					
						
							|  |  |  |     0x7c00, /* Inf */ | 
					
						
							|  |  |  |     0x7c01, /* NaN / AHP */ | 
					
						
							|  |  |  |     0x7cff, /* NaN / AHP */ | 
					
						
							|  |  |  |     0x7fff, /* NaN / AHP +Max*/ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const int num_f16 = ARRAY_SIZE(f16_numbers); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int get_num_f16(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return num_f16; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | uint16_t get_f16(int i) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return f16_numbers[i % num_f16]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* only display as hex */ | 
					
						
							|  |  |  | char *fmt_16(uint16_t num) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     char *fmt; | 
					
						
							|  |  |  |     asprintf(&fmt, "f16(%#04x)", num); | 
					
						
							|  |  |  |     return fmt; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Single Precision Numbers | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef SNANF
 | 
					
						
							|  |  |  | /* Signaling NaN macros, if supported.  */ | 
					
						
							|  |  |  | #  define SNANF (__builtin_nansf (""))
 | 
					
						
							|  |  |  | #  define SNAN (__builtin_nans (""))
 | 
					
						
							|  |  |  | #  define SNANL (__builtin_nansl (""))
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static float f32_numbers[] = { | 
					
						
							|  |  |  |     -SNANF, | 
					
						
							|  |  |  |     -NAN, | 
					
						
							|  |  |  |     -INFINITY, | 
					
						
							|  |  |  |     -FLT_MAX, | 
					
						
							|  |  |  |     -0x1.1874b2p+103, | 
					
						
							|  |  |  |     -0x1.c0bab6p+99, | 
					
						
							|  |  |  |     -0x1.31f75p-40, | 
					
						
							|  |  |  |     -0x1.505444p-66, | 
					
						
							|  |  |  |     -FLT_MIN, | 
					
						
							|  |  |  |     0.0, | 
					
						
							|  |  |  |     FLT_MIN, | 
					
						
							|  |  |  |     0x1p-25, | 
					
						
							|  |  |  |     0x1.ffffe6p-25, /* min positive FP16 subnormal */ | 
					
						
							|  |  |  |     0x1.ff801ap-15, /* max subnormal FP16 */ | 
					
						
							|  |  |  |     0x1.00000cp-14, /* min positive normal FP16 */ | 
					
						
							|  |  |  |     1.0, | 
					
						
							|  |  |  |     0x1.004p+0, /* smallest float after 1.0 FP16 */ | 
					
						
							|  |  |  |     2.0, | 
					
						
							|  |  |  |     M_E, M_PI, | 
					
						
							|  |  |  |     0x1.ffbep+15, | 
					
						
							|  |  |  |     0x1.ffcp+15, /* max FP16 */ | 
					
						
							|  |  |  |     0x1.ffc2p+15, | 
					
						
							|  |  |  |     0x1.ffbfp+16, | 
					
						
							|  |  |  |     0x1.ffcp+16, /* max AFP */ | 
					
						
							|  |  |  |     0x1.ffc1p+16, | 
					
						
							|  |  |  |     0x1.c0bab6p+99, | 
					
						
							|  |  |  |     FLT_MAX, | 
					
						
							|  |  |  |     INFINITY, | 
					
						
							|  |  |  |     NAN, | 
					
						
							|  |  |  |     SNANF | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const int num_f32 = ARRAY_SIZE(f32_numbers); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int get_num_f32(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return num_f32; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | float get_f32(int i) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return f32_numbers[i % num_f32]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | char *fmt_f32(float num) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint32_t single_as_hex = *(uint32_t *) # | 
					
						
							|  |  |  |     char *fmt; | 
					
						
							|  |  |  |     asprintf(&fmt, "f32(%02.20a:%#010x)", num, single_as_hex); | 
					
						
							|  |  |  |     return fmt; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* This allows us to initialise some doubles as pure hex */ | 
					
						
							|  |  |  | typedef union { | 
					
						
							|  |  |  |     double d; | 
					
						
							|  |  |  |     uint64_t h; | 
					
						
							|  |  |  | } test_doubles; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static test_doubles f64_numbers[] = { | 
					
						
							|  |  |  |     {SNAN}, | 
					
						
							|  |  |  |     {-NAN}, | 
					
						
							|  |  |  |     {-INFINITY}, | 
					
						
							|  |  |  |     {-DBL_MAX}, | 
					
						
							|  |  |  |     {-FLT_MAX-1.0}, | 
					
						
							|  |  |  |     {-FLT_MAX}, | 
					
						
							|  |  |  |     {-1.111E+31}, | 
					
						
							|  |  |  |     {-1.111E+30}, /* half prec */ | 
					
						
							|  |  |  |     {-2.0}, {-1.0}, | 
					
						
							|  |  |  |     {-DBL_MIN}, | 
					
						
							|  |  |  |     {-FLT_MIN}, | 
					
						
							|  |  |  |     {0.0}, | 
					
						
							|  |  |  |     {FLT_MIN}, | 
					
						
							|  |  |  |     {2.98023224e-08}, | 
					
						
							|  |  |  |     {5.96046E-8}, /* min positive FP16 subnormal */ | 
					
						
							|  |  |  |     {6.09756E-5}, /* max subnormal FP16 */ | 
					
						
							|  |  |  |     {6.10352E-5}, /* min positive normal FP16 */ | 
					
						
							|  |  |  |     {1.0}, | 
					
						
							|  |  |  |     {1.0009765625}, /* smallest float after 1.0 FP16 */ | 
					
						
							|  |  |  |     {DBL_MIN}, | 
					
						
							|  |  |  |     {1.3789972848607228e-308}, | 
					
						
							|  |  |  |     {1.4914738736681624e-308}, | 
					
						
							|  |  |  |     {1.0}, {2.0}, | 
					
						
							|  |  |  |     {M_E}, {M_PI}, | 
					
						
							|  |  |  |     {65503.0}, | 
					
						
							|  |  |  |     {65504.0}, /* max FP16 */ | 
					
						
							|  |  |  |     {65505.0}, | 
					
						
							|  |  |  |     {131007.0}, | 
					
						
							|  |  |  |     {131008.0}, /* max AFP */ | 
					
						
							|  |  |  |     {131009.0}, | 
					
						
							|  |  |  |     {.h = 0x41dfffffffc00000 }, /* to int = 0x7fffffff */ | 
					
						
							|  |  |  |     {FLT_MAX}, | 
					
						
							|  |  |  |     {FLT_MAX + 1.0}, | 
					
						
							|  |  |  |     {DBL_MAX}, | 
					
						
							|  |  |  |     {INFINITY}, | 
					
						
							|  |  |  |     {NAN}, | 
					
						
							|  |  |  |     {.h = 0x7ff0000000000001}, /* SNAN */ | 
					
						
							|  |  |  |     {SNAN}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const int num_f64 = ARRAY_SIZE(f64_numbers); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int get_num_f64(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return num_f64; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | double get_f64(int i) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return f64_numbers[i % num_f64].d; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | char *fmt_f64(double num) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint64_t double_as_hex = *(uint64_t *) # | 
					
						
							|  |  |  |     char *fmt; | 
					
						
							|  |  |  |     asprintf(&fmt, "f64(%02.20a:%#020" PRIx64 ")", num, double_as_hex); | 
					
						
							|  |  |  |     return fmt; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Float flags | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | char *fmt_flags(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int flags = fetestexcept(FE_ALL_EXCEPT); | 
					
						
							|  |  |  |     char *fmt; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (flags) { | 
					
						
							|  |  |  |         asprintf(&fmt, "%s%s%s%s%s", | 
					
						
							|  |  |  |                  flags & FE_OVERFLOW ? "OVERFLOW " : "", | 
					
						
							|  |  |  |                  flags & FE_UNDERFLOW ? "UNDERFLOW " : "", | 
					
						
							|  |  |  |                  flags & FE_DIVBYZERO ? "DIV0 " : "", | 
					
						
							|  |  |  |                  flags & FE_INEXACT ? "INEXACT " : "", | 
					
						
							|  |  |  |                  flags & FE_INVALID ? "INVALID" : ""); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         asprintf(&fmt, "OK"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return fmt; | 
					
						
							|  |  |  | } |