aboutsummaryrefslogtreecommitdiff
path: root/chad/terry.h
blob: 4a4ad5ddf0dbc35bfe7ca2d136c89867e69784b6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#ifndef TERRY_H
#define TERRY_H

#if 0
   Promises the most accurate primitives as are allowable by (recent) compiler version and C standard.
   Primitivie types:
   signed int : i8>i128, s8>s128
   unsigned int : u8>u128
   float  : f, f32
   double : fl, f64
   long double : fll, f128

   NO_128_TYPES will be defined when i128, s128, u128 are absent.
   NO_F_TYPE_NAME will remove hazardous declarations of f, fl, and fll.

   All floating bitmarked types will have F<NUM>_PRECISE defined
   if they are specified to that precise number of bits.

   Tested to work with gcc 14.2.0 and clang 19.1.7 on Debian // XXX: auto tests / auto comment
   Backwards compat for gcc 14.2.0
   Not robustly tested, please report issues.

   --- Definitions ---

   // XXX too *many* macros

   Defining the following will change behavior within the typesystem.
   the IMPRECISE groupings will always be provided with their respective types are present in full.

#define TERRY_FLOAT_IMPRECISE       /* supergroup of the below two groups */
#define TERRY_SMALL_FLOAT_IMPRECISE /* groups the two below together */
#define FLOAT_IS_F32
#define DOUBLE_IS_F64
#define TERRY_LARGE_FLOAT_IMPRECISE /* groups the two below together */
#define LONG_DOUBLE_IS_F80
#define LONG_DOUBLE_IS_F128
#define NO_128_TYPES                /* removes 128 declarations */
#define NO_F_TYPE_NAME 		    /* removes hazardous f, fl, fll */
#endif

/* --- */

#include <stdint.h>

typedef int8_t  i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;

typedef int8_t  s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;

typedef uint8_t  u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;

#ifndef NO_F_TYPE_NAME
typedef float  f;
typedef double fl;
typedef long double fll;
#endif

#ifndef NO_128_TYPES
# if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202000L && defined(__GNUC__) && !defined(__clang__)) \
  || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L)
#   define PRECISE_128
    typedef signed   _BitInt(128) i128;
    typedef signed   _BitInt(128) s128;
    typedef unsigned _BitInt(128) u128;
# else
#  if __SIZEOF_INT128__ 	/* compiler specific macro */
#    pragma GCC diagnostic push
#    pragma GCC diagnostic ignored "-Wpedantic"
#    define PRECISE_128
     typedef signed   __int128 i128;
     typedef signed   __int128 s128;
     typedef unsigned __int128 u128;
#    pragma GCC diagnostic pop
#  else
#    define NO_128_TYPES
#  endif
# endif
#endif

#if !(defined(__STDC_IEC_60559_BFP__) && defined(__STDC_IEC_60559_TYPES__))
#define TERRY_FLOAT_IMPRECISE
#endif

#ifdef TERRY_FLOAT_IMPRECISE
# define TERRY_SMALL_FLOAT_IMPRECISE
# define TERRY_LARGE_FLOAT_IMPRECISE
#endif

#ifdef TERRY_SMALL_FLOAT_IMPRECISE
# define FLOAT_IS_F32
# define DOUBLE_IS_F64
#endif

#ifdef TERRY_LARGE_FLOAT_IMPRECISE
# define LONG_DOUBLE_IS_F80
# define LONG_DOUBLE_IS_F128
#endif

/* You can treat floats like _Float32, and doubles like _Float64, however *THEY ARE NOT THE SAME THING*. */
/* go ask WG14 for the "why." GCC/Clang may complain but will work as expected. */

#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202000L && defined(__GNUC__) && !defined(__clang__)) \
||  (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L)
# if defined(FLOAT_IS_F32) || defined(TERRY_SMALL_FLOAT_IMPRECISE)
    typedef float f32;
# else
#   define F32_PRECISE
    typedef _Float32 f32;
# endif

# if defined(DOUBLE_IS_F64) || defined(TERRY_LARGE_FLOAT_IMPRECISE)
    typedef double f64;
# else
#   define F64_PRECISE
    typedef _Float64 f64;
# endif

# if defined(__GNUC__) && !defined(__clang__)
#   define F80_PRECISE
    typedef __float80 f80;
# else
    #define LONG_DOUBLE_IS_F80
    typedef long double f80;
# endif

# if defined(LONG_DOUBLE_IS_F128) || defined(TERRY_LARGE_FLOAT_IMPRECISE)
    typedef long double f128;
# else
#   define F128_PRECISE
    typedef _Float128 f128;
# endif
#else
/* the below f32/64 definitions may behave erratically, as float != f32 && double != f64.
 * We're just doing this for the sake of defining something that will *probably* work in most cases.
 * Use newer C versions. */
# if defined(__GNUC__) && !defined(__clang__)
#   define TERRY_SMALL_FLOAT_IMPRECISE

#   define FLOAT_IS_F32
    typedef float  f32;
#   define DOUBLE_IS_F64
    typedef double f64;

#   if defined(LONG_DOUBLE_IS_F80)
      typedef long double  f80;
#   else
#     define F80_PRECISE
      typedef __float80 f80;
#   endif

#   if defined(LONG_DOUBLE_IS_F128)
      typedef long double  f128;
#   else
#     define F128_PRECISE
      typedef __float128 f128;
#   endif
# else
#   define TERRY_FLOAT_IMPRECISE
#   define TERRY_SMALL_FLOAT_IMPRECISE
#   define TERRY_LARGE_FLOAT_IMPRECISE
#   define FLOAT_IS_F32
    typedef float  f32;
#   define DOUBLE_IS_F64
    typedef double f64;
    /* Usually 10-bytes with 128-bit alignment */
#   define LONG_DOUBLE_IS_F80
    typedef long double f80;
#   define LONG_DOUBLE_IS_F128
    typedef long double f128;
# endif
#endif
#endif