blob: 20057c32fbaabf7e7f8fb94f8fe16badeea0fefd [file] [log] [blame]
Laurence Lundblade6ed34222018-12-18 09:46:23 -08001/*==============================================================================
2 float_tests.c -- tests for float and conversion to/from half-precision
3
Laurence Lundbladeac515e52020-01-30 10:44:06 -08004 Copyright (c) 2018-2020, Laurence Lundblade. All rights reserved.
Laurence Lundblade6ed34222018-12-18 09:46:23 -08005
6 SPDX-License-Identifier: BSD-3-Clause
7
8 See BSD-3-Clause license in README.md
9
10 Created on 9/19/18
Laurence Lundbladeac515e52020-01-30 10:44:06 -080011 =============================================================================*/
Laurence Lundblade6ed34222018-12-18 09:46:23 -080012
13#include "float_tests.h"
14#include "qcbor.h"
15#include "half_to_double_from_rfc7049.h"
16#include <math.h> // For INFINITY and NAN and isnan()
17
18
19
20static const uint8_t spExpectedHalf[] = {
21 0xB1,
22 0x64,
23 0x7A, 0x65, 0x72, 0x6F,
24 0xF9, 0x00, 0x00, // 0.000
25 0x6A,
26 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
27 0xF9, 0x7C, 0x00, // Infinity
28 0x73,
Laurence Lundbladeac515e52020-01-30 10:44:06 -080029 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6E,
30 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
Laurence Lundblade6ed34222018-12-18 09:46:23 -080031 0xF9, 0xFC, 0x00, // -Inifinity
32 0x63,
33 0x4E, 0x61, 0x4E,
34 0xF9, 0x7E, 0x00, // NaN
35 0x63,
36 0x6F, 0x6E, 0x65,
37 0xF9, 0x3C, 0x00, // 1.0
38 0x69,
39 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
40 0xF9, 0x35, 0x55, // 0.333251953125
41 0x76,
Laurence Lundbladeac515e52020-01-30 10:44:06 -080042 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C,
43 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
Laurence Lundblade6ed34222018-12-18 09:46:23 -080044 0xF9, 0x7B, 0xFF, // 65504.0
Laurence Lundbladeac515e52020-01-30 10:44:06 -080045 0x78, 0x18,
46 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x68,
47 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69,
48 0x6F, 0x6E,
Laurence Lundblade6ed34222018-12-18 09:46:23 -080049 0xF9, 0x7C, 0x00, // Infinity
50 0x72,
Laurence Lundbladeac515e52020-01-30 10:44:06 -080051 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
52 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
Laurence Lundblade6ed34222018-12-18 09:46:23 -080053 0xF9, 0x00, 0x01, // 0.000000059604
54 0x6F,
Laurence Lundbladeac515e52020-01-30 10:44:06 -080055 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
56 0x72, 0x6D, 0x61, 0x6C,
Laurence Lundblade6ed34222018-12-18 09:46:23 -080057 0xF9, 0x03, 0xFF, // 0.0000609755516
58 0x71,
Laurence Lundbladeac515e52020-01-30 10:44:06 -080059 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62,
60 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
Laurence Lundblade6ed34222018-12-18 09:46:23 -080061 0xF9, 0x04, 0x00, // 0.000061988
62 0x70,
Laurence Lundbladeac515e52020-01-30 10:44:06 -080063 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x73,
64 0x69, 0x6E, 0x67, 0x6C, 0x65,
Laurence Lundblade6ed34222018-12-18 09:46:23 -080065 0xF9, 0x00, 0x00,
66 0x03,
67 0xF9, 0xC0, 0x00, // -2
68 0x04,
69 0xF9, 0x7E, 0x00, // qNaN
70 0x05,
71 0xF9, 0x7C, 0x01, // sNaN
72 0x06,
73 0xF9, 0x7E, 0x0F, // qNaN with payload 0x0f
74 0x07,
75 0xF9, 0x7C, 0x0F, // sNaN with payload 0x0f
76
77};
78
79
Laurence Lundbladeac515e52020-01-30 10:44:06 -080080int32_t HalfPrecisionDecodeBasicTests()
Laurence Lundblade6ed34222018-12-18 09:46:23 -080081{
82 UsefulBufC HalfPrecision = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedHalf);
83
84 QCBORDecodeContext DC;
Laurence Lundblade234fe422019-12-02 13:04:34 -080085 QCBORDecode_Init(&DC, HalfPrecision, 0);
Laurence Lundblade6ed34222018-12-18 09:46:23 -080086
87 QCBORItem Item;
88
89 QCBORDecode_GetNext(&DC, &Item);
90 if(Item.uDataType != QCBOR_TYPE_MAP) {
91 return -1;
92 }
93
94 QCBORDecode_GetNext(&DC, &Item);
95 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0F) {
96 return -2;
97 }
98
99 QCBORDecode_GetNext(&DC, &Item);
100 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
101 return -3;
102 }
103
104 QCBORDecode_GetNext(&DC, &Item);
105 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -INFINITY) {
106 return -4;
107 }
108
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800109 // TODO, is this really converting right? It is carrying payload, but
110 // this confuses things.
111 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800112 if(Item.uDataType != QCBOR_TYPE_DOUBLE || !isnan(Item.val.dfnum)) {
113 return -5;
114 }
115
116 QCBORDecode_GetNext(&DC, &Item);
117 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 1.0F) {
118 return -6;
119 }
120
121 QCBORDecode_GetNext(&DC, &Item);
122 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.333251953125F) {
123 return -7;
124 }
125
126 QCBORDecode_GetNext(&DC, &Item);
127 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 65504.0F) {
128 return -8;
129 }
130
131 QCBORDecode_GetNext(&DC, &Item);
132 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
133 return -9;
134 }
135
136 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
137 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000000596046448F) {
138 return -10;
139 }
140
141 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
142 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000609755516F) {
143 return -11;
144 }
145
146 QCBORDecode_GetNext(&DC, &Item); // TODO check this
147 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000610351563F) {
148 return -12;
149 }
150
151 QCBORDecode_GetNext(&DC, &Item);
152 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0) {
153 return -13;
154 }
155
156 QCBORDecode_GetNext(&DC, &Item);
157 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -2.0F) {
158 return -14;
159 }
160
161 // TODO: double check these four tests
162 QCBORDecode_GetNext(&DC, &Item); // qNaN
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800163 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
164 UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff8000000000000ULL) {
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800165 return -15;
166 }
167 QCBORDecode_GetNext(&DC, &Item); // sNaN
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800168 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
169 UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff0000000000001ULL) {
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800170 return -16;
171 }
172 QCBORDecode_GetNext(&DC, &Item); // qNaN with payload 0x0f
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800173 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
174 UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff800000000000fULL) {
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800175 return -17;
176 }
177 QCBORDecode_GetNext(&DC, &Item); // sNaN with payload 0x0f
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800178 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
179 UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff000000000000fULL) {
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800180 return -18;
181 }
182
183 if(QCBORDecode_Finish(&DC)) {
184 return -19;
185 }
186
187 return 0;
188}
189
190
191
192
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800193int32_t HalfPrecisionAgainstRFCCodeTest()
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800194{
195 for(uint32_t uHalfP = 0; uHalfP < 0xffff; uHalfP += 60) {
196 unsigned char x[2];
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800197 x[1] = (uint8_t)(uHalfP & 0xff);
198 x[0] = (uint8_t)(uHalfP >> 8); // uHalfP is always less than 0xffff
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800199 double d = decode_half(x);
200
201 // Contruct the CBOR for the half-precision float by hand
202 UsefulBuf_MAKE_STACK_UB(__xx, 3);
203 UsefulOutBuf UOB;
204 UsefulOutBuf_Init(&UOB, __xx);
205
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800206 const uint8_t uHalfPrecInitialByte = (uint8_t)(HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5)); // 0xf9
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800207 UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
208 UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
209
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800210 // Now parse the hand-constructed CBOR. This will invoke the
211 // conversion to a float
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800212 QCBORDecodeContext DC;
Laurence Lundblade234fe422019-12-02 13:04:34 -0800213 QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800214
215 QCBORItem Item;
216
217 QCBORDecode_GetNext(&DC, &Item);
218 if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
219 return -1;
220 }
221
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800222 //printf("%04x QCBOR:%15.15f RFC: %15.15f (%8x)\n",
223 // uHalfP, Item.val.fnum, d , UsefulBufUtil_CopyFloatToUint32(d));
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800224
225 if(isnan(d)) {
226 // The RFC code uses the native instructions which may or may not
227 // handle sNaN, qNaN and NaN payloads correctly. This test just
228 // makes sure it is a NaN and doesn't worry about the type of NaN
229 if(!isnan(Item.val.dfnum)) {
230 return -3;
231 }
232 } else {
233 if(Item.val.dfnum != d) {
234 return -2;
235 }
236 }
237 }
238 return 0;
239}
240
241
242/*
243 {"zero": 0.0,
244 "negative zero": -0.0,
245 "infinitity": Infinity,
246 "negative infinitity": -Infinity,
247 "NaN": NaN,
248 "one": 1.0,
249 "one third": 0.333251953125,
250 "largest half-precision": 65504.0,
251 "largest half-precision point one": 65504.1,
252 "too-large half-precision": 65536.0,
253 "smallest subnormal": 5.96046448e-8,
254 "smallest normal": 0.00006103515261202119,
255 "biggest subnormal": 0.00006103515625,
256 "subnormal single": 4.00000646641519e-40,
257 3: -2.0,
258 "large single exp": 2.5521177519070385e+38,
259 "too-large single exp": 5.104235503814077e+38,
260 "biggest single with prec": 16777216.0,
261 "first single with prec loss": 16777217.0,
262 1: "fin"}
263 */
264static const uint8_t spExpectedSmallest[] = {
265 0xB4, 0x64, 0x7A, 0x65, 0x72, 0x6F, 0xF9, 0x00, 0x00, 0x6D,
266 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x7A,
267 0x65, 0x72, 0x6F, 0xF9, 0x80, 0x00, 0x6A, 0x69, 0x6E, 0x66,
268 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79, 0xF9, 0x7C, 0x00,
269 0x73, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20,
270 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
271 0xF9, 0xFC, 0x00, 0x63, 0x4E, 0x61, 0x4E, 0xF9, 0x7E, 0x00,
272 0x63, 0x6F, 0x6E, 0x65, 0xF9, 0x3C, 0x00, 0x69, 0x6F, 0x6E,
273 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0xF9, 0x35, 0x55,
274 0x76, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68,
275 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73,
276 0x69, 0x6F, 0x6E, 0xF9, 0x7B, 0xFF, 0x78, 0x20, 0x6C, 0x61,
277 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66,
278 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
279 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, 0x6F, 0x6E, 0x65,
280 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33, 0x78,
281 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65,
282 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63,
283 0x69, 0x73, 0x69, 0x6F, 0x6E, 0xFA, 0x47, 0x80, 0x00, 0x00,
284 0x72, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
285 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xFB,
286 0x3E, 0x70, 0x00, 0x00, 0x00, 0x1C, 0x5F, 0x68, 0x6F, 0x73,
287 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
288 0x72, 0x6D, 0x61, 0x6C, 0xFA, 0x38, 0x7F, 0xFF, 0xFF, 0x71,
289 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
290 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xF9, 0x04, 0x00,
291 0x70, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
292 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0xFB, 0x37, 0xC1,
293 0x6C, 0x28, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF9, 0xC0, 0x00,
294 0x70, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E,
295 0x67, 0x6C, 0x65, 0x20, 0x65, 0x78, 0x70, 0xFA, 0x7F, 0x40,
296 0x00, 0x00, 0x74, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72,
297 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20,
298 0x65, 0x78, 0x70, 0xFB, 0x47, 0xF8, 0x00, 0x00, 0x00, 0x00,
299 0x00, 0x00, 0x78, 0x18, 0x62, 0x69, 0x67, 0x67, 0x65, 0x73,
300 0x74, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77,
301 0x69, 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0xFA, 0x4B,
302 0x80, 0x00, 0x00, 0x78, 0x1B, 0x66, 0x69, 0x72, 0x73, 0x74,
303 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77, 0x69,
304 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x20, 0x6C, 0x6F,
305 0x73, 0x73, 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00,
306 0x00, 0x01, 0x63, 0x66, 0x69, 0x6E
307};
308
309
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800310int32_t DoubleAsSmallestTest()
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800311{
312 UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, 420);
313
314#define QCBOREncode_AddDoubleAsSmallestToMap QCBOREncode_AddDoubleToMap
315#define QCBOREncode_AddDoubleAsSmallestToMapN QCBOREncode_AddDoubleToMapN
316
317
318 QCBOREncodeContext EC;
319 QCBOREncode_Init(&EC, EncodedHalfsMem);
320 // These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
321 QCBOREncode_OpenMap(&EC);
322 // 64 # text(4)
323 // 7A65726F # "zero"
324 // F9 0000 # primitive(0)
325 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "zero", 0.00);
326
327 // 64 # text(4)
328 // 7A65726F # "negative zero"
329 // F9 8000 # primitive(0)
330 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative zero", -0.00);
331
332 // 6A # text(10)
333 // 696E66696E6974697479 # "infinitity"
334 // F9 7C00 # primitive(31744)
335 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "infinitity", INFINITY);
336
337 // 73 # text(19)
338 // 6E6567617469766520696E66696E6974697479 # "negative infinitity"
339 // F9 FC00 # primitive(64512)
340 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative infinitity", -INFINITY);
341
342 // 63 # text(3)
343 // 4E614E # "NaN"
344 // F9 7E00 # primitive(32256)
345 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "NaN", NAN);
346
347 // TODO: test a few NaN variants
348
349 // 63 # text(3)
350 // 6F6E65 # "one"
351 // F9 3C00 # primitive(15360)
352 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one", 1.0);
353
354 // 69 # text(9)
355 // 6F6E65207468697264 # "one third"
356 // F9 3555 # primitive(13653)
357 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one third", 0.333251953125);
358
359 // 76 # text(22)
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800360 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800361 // F9 7BFF # primitive(31743)
362 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision",65504.0);
363
364 // 76 # text(22)
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800365 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800366 // F9 7BFF # primitive(31743)
367 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision point one",65504.1);
368
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800369 // Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which
370 // is larger than 15, the largest half-precision exponent
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800371 // 78 18 # text(24)
372 // 746F6F2D6C617267652068616C662D707265636973696F6E # "too-large half-precision"
373 // FA 47800000 # primitive(31743)
374 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large half-precision", 65536.0);
375
376 // The smallest possible half-precision subnormal, but digitis are lost converting
377 // to half, so this turns into a double
378 // 72 # text(18)
379 // 736D616C6C657374207375626E6F726D616C # "smallest subnormal"
380 // FB 3E700000001C5F68 # primitive(4499096027744984936)
381 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest subnormal", 0.0000000596046448);
382
383 // The smallest possible half-precision snormal, but digitis are lost converting
384 // to half, so this turns into a single TODO: confirm this is right
385 // 6F # text(15)
386 // 736D616C6C657374206E6F726D616C # "smallest normal"
387 // FA 387FFFFF # primitive(947912703)
388 // in hex single is 0x387fffff, exponent -15, significand 7fffff
389 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest normal", 0.0000610351526F);
390
391 // 71 # text(17)
392 // 62696767657374207375626E6F726D616C # "biggest subnormal"
393 // F9 0400 # primitive(1024)
394 // in hex single is 0x38800000, exponent -14, significand 0
395 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest subnormal", 0.0000610351563F);
396
397 // 70 # text(16)
398 // 7375626E6F726D616C2073696E676C65 # "subnormal single"
399 // FB 37C16C2800000000 # primitive(4017611261645684736)
400 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "subnormal single", 4e-40F);
401
402 // 03 # unsigned(3)
403 // F9 C000 # primitive(49152)
404 QCBOREncode_AddDoubleAsSmallestToMapN(&EC, 3, -2.0);
405
406 // 70 # text(16)
407 // 6C617267652073696E676C6520657870 # "large single exp"
408 // FA 7F400000 # primitive(2134900736)
409 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((127LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
410 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "large single exp", 2.5521177519070385E+38); // Exponent fits single
411
412 // 74 # text(20)
413 // 746F6F2D6C617267652073696E676C6520657870 # "too-large single exp"
414 // FB 47F8000000000000 # primitive(5185894970917126144)
415 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((128LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800416 // Exponent too large for single
417 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large single exp", 5.104235503814077E+38);
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800418
419 // 66 # text(6)
420 // 646664666465 # "dfdfde"
421 // FA 4B800000 # primitive(1266679808)
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800422 // Single with no precision loss
423 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest single with prec", 16777216);
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800424
425 // 78 18 # text(24)
426 // 626967676573742073696E676C6520776974682070726563 # "biggest single with prec"
427 // FA 4B800000 # primitive(1266679808)
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800428 // Double becuase of precision loss
429 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "first single with prec loss", 16777217);
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800430
431 // Just a convenient marker when cutting and pasting encoded CBOR
432 QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
433
434 QCBOREncode_CloseMap(&EC);
435
436 UsefulBufC EncodedHalfs;
437 int nReturn = QCBOREncode_Finish(&EC, &EncodedHalfs);
438 if(nReturn) {
439 return -1;
440 }
441
442 if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedSmallest))) {
443 return -3;
444 }
445
446 return 0;
447}
448
449
450
451#ifdef NAN_EXPERIMENT
452/*
453 Code for checking what the double to float cast does with
454 NaNs. Not run as part of tests. Keep it around to
455 be able to check various platforms and CPUs.
456 */
457
458#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
459#define DOUBLE_NUM_EXPONENT_BITS (11)
460#define DOUBLE_NUM_SIGN_BITS (1)
461
462#define DOUBLE_SIGNIFICAND_SHIFT (0)
463#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
464#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
465
466#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
467#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
468#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
469#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
470
471
472static int NaNExperiments() {
473 double dqNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT);
474 double dsNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | 0x01);
475 double dqNaNPayload = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT | 0xf00f);
476
477 float f1 = (float)dqNaN;
478 float f2 = (float)dsNaN;
479 float f3 = (float)dqNaNPayload;
480
481
482 uint32_t uqNaN = UsefulBufUtil_CopyFloatToUint32((float)dqNaN);
483 uint32_t usNaN = UsefulBufUtil_CopyFloatToUint32((float)dsNaN);
484 uint32_t uqNaNPayload = UsefulBufUtil_CopyFloatToUint32((float)dqNaNPayload);
485
486 // Result of this on x86 is that every NaN is a qNaN. The intel
487 // CVTSD2SS instruction ignores the NaN payload and even converts
488 // a sNaN to a qNaN.
489
490 return 0;
491}
492#endif
493
494
495