blob: aaf7f4909c30250d89da694bda86af19ba068396 [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;
TTornblomfaf74f52020-03-04 17:56:27 +010085 QCBORDecode_Init(&DC, HalfPrecision, QCBOR_DECODE_MODE_NORMAL);
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;
TTornblomfaf74f52020-03-04 17:56:27 +0100213 QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), QCBOR_DECODE_MODE_NORMAL);
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
TTornblome3ef1fd2020-01-23 16:50:19 +0100397#if !defined(__ICCARM__) || (__SUBNORMAL_FLOATING_POINTS__ == 1)
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800398 // 70 # text(16)
399 // 7375626E6F726D616C2073696E676C65 # "subnormal single"
400 // FB 37C16C2800000000 # primitive(4017611261645684736)
401 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "subnormal single", 4e-40F);
TTornblome3ef1fd2020-01-23 16:50:19 +0100402#endif
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800403
404 // 03 # unsigned(3)
405 // F9 C000 # primitive(49152)
406 QCBOREncode_AddDoubleAsSmallestToMapN(&EC, 3, -2.0);
407
408 // 70 # text(16)
409 // 6C617267652073696E676C6520657870 # "large single exp"
410 // FA 7F400000 # primitive(2134900736)
411 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((127LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
412 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "large single exp", 2.5521177519070385E+38); // Exponent fits single
413
414 // 74 # text(20)
415 // 746F6F2D6C617267652073696E676C6520657870 # "too-large single exp"
416 // FB 47F8000000000000 # primitive(5185894970917126144)
417 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((128LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800418 // Exponent too large for single
419 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large single exp", 5.104235503814077E+38);
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800420
421 // 66 # text(6)
422 // 646664666465 # "dfdfde"
423 // FA 4B800000 # primitive(1266679808)
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800424 // Single with no precision loss
425 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest single with prec", 16777216);
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800426
427 // 78 18 # text(24)
428 // 626967676573742073696E676C6520776974682070726563 # "biggest single with prec"
429 // FA 4B800000 # primitive(1266679808)
Laurence Lundbladeac515e52020-01-30 10:44:06 -0800430 // Double becuase of precision loss
431 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "first single with prec loss", 16777217);
Laurence Lundblade6ed34222018-12-18 09:46:23 -0800432
433 // Just a convenient marker when cutting and pasting encoded CBOR
434 QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
435
436 QCBOREncode_CloseMap(&EC);
437
438 UsefulBufC EncodedHalfs;
439 int nReturn = QCBOREncode_Finish(&EC, &EncodedHalfs);
440 if(nReturn) {
441 return -1;
442 }
443
444 if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedSmallest))) {
445 return -3;
446 }
447
448 return 0;
449}
450
451
452
453#ifdef NAN_EXPERIMENT
454/*
455 Code for checking what the double to float cast does with
456 NaNs. Not run as part of tests. Keep it around to
457 be able to check various platforms and CPUs.
458 */
459
460#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
461#define DOUBLE_NUM_EXPONENT_BITS (11)
462#define DOUBLE_NUM_SIGN_BITS (1)
463
464#define DOUBLE_SIGNIFICAND_SHIFT (0)
465#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
466#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
467
468#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
469#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
470#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
471#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
472
473
474static int NaNExperiments() {
475 double dqNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT);
476 double dsNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | 0x01);
477 double dqNaNPayload = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT | 0xf00f);
478
479 float f1 = (float)dqNaN;
480 float f2 = (float)dsNaN;
481 float f3 = (float)dqNaNPayload;
482
483
484 uint32_t uqNaN = UsefulBufUtil_CopyFloatToUint32((float)dqNaN);
485 uint32_t usNaN = UsefulBufUtil_CopyFloatToUint32((float)dsNaN);
486 uint32_t uqNaNPayload = UsefulBufUtil_CopyFloatToUint32((float)dqNaNPayload);
487
488 // Result of this on x86 is that every NaN is a qNaN. The intel
489 // CVTSD2SS instruction ignores the NaN payload and even converts
490 // a sNaN to a qNaN.
491
492 return 0;
493}
494#endif
495
496
497