@@ -26,12 +26,13 @@ static_assert(4 == sizeof(float32_t), "Invalid `float32_t` size.");
2626typedef double float64_t ;
2727static_assert (8 == sizeof (float64_t ), " Invalid `float64_t` size." );
2828
29+ // TODO: this is 100% incorrect
2930typedef double float128_t ;
3031static_assert (8 == sizeof (float128_t ), " Invalid `float128_t` size." );
3132
3233// a long double can be anything from a 128-bit float (on AArch64/Linux) to a 64-bit double (AArch64 MacOS)
3334// to an 80-bit precision wrapped with padding (x86/x86-64). We do not do a static assert on the size
34- // since there are too many options.
35+ // since there are too many options.
3536
3637// A "native_float80_t" is a native type that is closes to approximating
3738// an x86 80-bit float.
@@ -42,76 +43,68 @@ static_assert(8 == sizeof(float128_t), "Invalid `float128_t` size.");
4243 #else
4344 typedef long double native_float80_t ;
4445 #endif
45- static_assert (10 <= sizeof (native_float80_t ), " Invalid `native_float80_t` size." );
46+ static_assert (sizeof (native_float80_t ) >= 10 , " Invalid `native_float80_t` size." );
4647#else
4748 typedef double native_float80_t ;
48- static_assert (8 == sizeof (native_float80_t ), "Invalid `native_float80_t` size.");
49+ static_assert (sizeof (native_float80_t ) == 8 , "Invalid `native_float80_t` size.");
4950#endif
5051
51- static const int kEightyBitsInBytes = 10 ;
52- union union_ld {
53- struct {
54- uint8_t data[kEightyBitsInBytes ];
55- // when building against CUDA, default to 64-bit float80s
56- #if !defined(__CUDACC__) && !defined(_WIN32) && (defined(__x86_64__) || defined(__i386__) || defined(_M_X86))
57- // We are doing x86 on x86, so we have native x86 FP80s, but they
58- // are not available in raw 80-bit native form.
59- //
60- // To get to the internal FP80 representation, we have to use a
61- // `long double` which is (usually! but not always)
62- // an FP80 padded to a 12 or 16 byte boundary
63- //
64- uint8_t padding[sizeof (native_float80_t ) - kEightyBitsInBytes ];
65- #else
66- // The closest native FP type that we can easily deal with is a 64-bit double
67- // this is less than the size of an FP80, so the data variable above will already
68- // enclose it. No extra padding is needed
69- #endif
70- } lds __attribute__ ((packed));
71- native_float80_t ld;
72- } __attribute__((packed));
73-
74- static void *memset_impl (void *b, int c, std::size_t len) {
75- auto *p = static_cast <int *>(b);
76- for (std::size_t i = 0 ; i < len; ++i) {
77- p[i] = c;
78- }
79- return b;
80- }
81-
82- static void *memcpy_impl (void *dst, const void *src, std::size_t n) {
83- auto *d = static_cast <int *>(dst);
84- const auto *s = static_cast <const int *>(src);
85- for (std::size_t i = 0 ; i < n; ++i) {
86- d[i] = s[i];
87- }
88- return dst;
89- }
90-
9152struct float80_t final {
92- uint8_t data[kEightyBitsInBytes ];
53+ uint8_t data[10 ];
9354
94- inline ~float80_t (void ) = default ;
95- inline float80_t (void ) : data{0 ,} {}
55+ ~float80_t (void ) = default ;
56+ float80_t (void ) : data{0 ,} {}
9657
9758 float80_t (const float80_t &) = default ;
9859 float80_t &operator =(const float80_t &) = default ;
9960
100- inline float80_t (native_float80_t ld) {
101- union_ld ldu;
102- memset_impl (&ldu, 0 , sizeof (ldu)); // zero out ldu to make padding consistent
103- ldu.ld = ld; // assign native value
104- // copy the representation to this object
105- memcpy_impl (&data[0 ], &ldu.lds .data [0 ], sizeof (data));
61+ float80_t (native_float80_t ld) {
62+ if constexpr (sizeof (ld) < sizeof (data)) {
63+ // Native floats are smaller than 80 bits, add padding
64+ memcpy_impl (data, &ld, sizeof (ld));
65+ memset_impl (data + sizeof (ld), 0 , sizeof (data) - sizeof (ld));
66+ } else {
67+ // Native floats are bigger than 80 bits, truncate
68+ memcpy_impl (data, &ld, sizeof (data));
69+ }
10670 }
10771
10872 operator native_float80_t () {
109- union_ld ldu;
110- memset_impl (&ldu, 0 , sizeof (ldu)); // zero out ldu to make padding consistent
111- // copy the internal representation into the union
112- memcpy_impl (&ldu.lds .data [0 ], &data[0 ], sizeof (data));
113- // extract the native backing type from it
114- return ldu.ld ;
73+ native_float80_t nf;
74+ if constexpr (sizeof (nf) < sizeof (data)) {
75+ // Native floats are smaller than 80 bits, truncate
76+ memcpy_impl (&nf, data, sizeof (nf));
77+ } else {
78+ // Native floats are bigger than 80 bits, add padding
79+ memcpy_impl ((unsigned char *)&nf, data, sizeof (data));
80+ memset_impl ((unsigned char *)&nf + sizeof (data), 0 , sizeof (nf) - sizeof (data));
81+ }
82+ return nf;
83+ }
84+
85+ static void *memset_impl (void *b, int c, std::size_t len) {
86+ #if defined(__clang__) || defined(__GNUC__)
87+ return __builtin_memset (b, c, len);
88+ #else
89+ auto *p = static_cast <int *>(b);
90+ for (std::size_t i = 0 ; i < len; ++i) {
91+ p[i] = c;
92+ }
93+ return b;
94+ #endif
95+ }
96+
97+ static void *memcpy_impl (void *dst, const void *src, std::size_t n) {
98+ #if defined(__clang__) || defined(__GNUC__)
99+ return __builtin_memcpy (dst, src, n);
100+ #else
101+ auto *d = static_cast <int *>(dst);
102+ const auto *s = static_cast <const int *>(src);
103+ for (std::size_t i = 0 ; i < n; ++i) {
104+ d[i] = s[i];
105+ }
106+ return dst;
107+ #endif
115108 }
116109} __attribute__((packed));
117110
@@ -147,8 +140,8 @@ union nan80_t {
147140 float80_t d;
148141 struct {
149142 uint64_t payload : 62 ;
150- uint64_t is_quiet_nan : 1 ;
151- uint64_t interger_bit : 1 ;
143+ uint64_t is_quiet_nan : 1 ;
144+ uint64_t interger_bit : 1 ;
152145 uint64_t exponent : 15 ;
153146 uint64_t is_negative : 1 ;
154147 } __attribute__ ((packed));
0 commit comments