blob: 4038cd6fb5ef91b0cabbd65c86818c47bfc87dc0 [file] [log] [blame]
Laurence Lundblade68a13352018-09-23 02:19:54 -07001/*==============================================================================
2 Copyright 2018 Laurence Lundblade
3
4 Permission is hereby granted, free of charge, to any person obtaining
5 a copy of this software and associated documentation files (the
6 "Software"), to deal in the Software without restriction, including
7 without limitation the rights to use, copy, modify, merge, publish,
8 distribute, sublicense, and/or sell copies of the Software, and to
9 permit persons to whom the Software is furnished to do so, subject to
10 the following conditions:
11
12 The above copyright notice and this permission notice shall be included
13 in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23
24 (This is the MIT license)
25 ==============================================================================*/
26//
27// half_precision_test.c
28// QCBOR
29//
30// Created by Laurence Lundblade on 9/19/18.
31// Copyright © 2018 Laurence Lundblade. All rights reserved.
32//
33
34#include "half_precision_test.h"
35#include "qcbor.h"
Laurence Lundbladed711fb22018-09-26 14:35:22 -070036#include "half_to_double_from_rfc7049.h"
37#include <math.h> // For INFINITY and NAN and isnan()
Laurence Lundblade68a13352018-09-23 02:19:54 -070038
39static const uint8_t ExpectedHalf[] = {
40 0xAD,
41 0x64,
42 0x7A, 0x65, 0x72, 0x6F,
43 0xF9, 0x00, 0x00, // 0.000
44 0x6A,
45 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
46 0xF9, 0x7C, 0x00, // Infinity
47 0x73,
48 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
49 0xF9, 0xFC, 0x00, // -Inifinity
50 0x63,
51 0x4E, 0x61, 0x4E,
52 0xF9, 0x7E, 0x00, // NaN
53 0x63,
54 0x6F, 0x6E, 0x65,
55 0xF9, 0x3C, 0x00, // 1.0
56 0x69,
57 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
58 0xF9, 0x35, 0x55, // 0.333251953125
59 0x76,
60 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
61 0xF9, 0x7B, 0xFF, // 65504.0
62 0x78, 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
63 0xF9, 0x7C, 0x00, // Infinity
64 0x72,
65 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
66 0xF9, 0x00, 0x01, // 0.000000059604
67 0x6F,
68 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
69 0xF9, 0x03, 0xFF, // 0.0000609755516
70 0x71,
71 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
72 0xF9, 0x04, 0x00, // 0.000061988
73 0x70,
74 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65,
75 0xF9, 0x00, 0x00,
76 0x03,
77 0xF9, 0xC0, 0x00 // -2.0
78};
79
80
81
82int half_precision_encode_basic()
83{
84 UsefulBuf_MakeStackUB(EncodedHalfsMem, 220);
85
86 QCBOREncodeContext EC;
87 QCBOREncode_Init(&EC, EncodedHalfsMem);
88 // These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
89 QCBOREncode_OpenMap(&EC);
90 QCBOREncode_AddFloatAsHalfToMap(&EC, "zero", 0.00F);
91 QCBOREncode_AddFloatAsHalfToMap(&EC, "infinitity", INFINITY);
92 QCBOREncode_AddFloatAsHalfToMap(&EC, "negative infinitity", -INFINITY);
93 QCBOREncode_AddFloatAsHalfToMap(&EC, "NaN", NAN);
94 QCBOREncode_AddFloatAsHalfToMap(&EC, "one", 1.0F);
95 QCBOREncode_AddFloatAsHalfToMap(&EC, "one third", 0.333251953125F);
96 QCBOREncode_AddFloatAsHalfToMap(&EC, "largest half-precision",65504.0F);
97 // Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which is larger than 15, the largest half-precision exponent
98 QCBOREncode_AddFloatAsHalfToMap(&EC, "too-large half-precision", 65536.0F);
99 // Should convert to smallest possible half precision which is encodded as 0x00 0x01 or 5.960464477539063e-8
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700100 QCBOREncode_AddFloatAsHalfToMap(&EC, "smallest subnormal", 0.0000000596046448F);
Laurence Lundblade68a13352018-09-23 02:19:54 -0700101 QCBOREncode_AddFloatAsHalfToMap(&EC, "smallest normal", 0.0000610351526F); // in hex single is 0x387fffff, exponent -15, significand 7fffff
102 QCBOREncode_AddFloatAsHalfToMap(&EC, "biggest subnormal", 0.0000610351563F); // in hex single is 0x38800000, exponent -14, significand 0
103 QCBOREncode_AddFloatAsHalfToMap(&EC, "subnormal single", 4e-40F);
104 QCBOREncode_AddFloatAsHalfToMapN(&EC, 3, -2.0F);
105 QCBOREncode_CloseMap(&EC);
106
107 EncodedCBOR EncodedHalfs;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700108 int nReturn = QCBOREncode_Finish2(&EC, &EncodedHalfs);
Laurence Lundblade68a13352018-09-23 02:19:54 -0700109 if(nReturn) {
110 return -1;
111 }
112
113 if(UsefulBuf_Compare(EncodedHalfs.Bytes, UsefulBuf_FromByteArrayLiteral(ExpectedHalf))) {
114 return -3;
115 }
116
117 return 0;
118}
119
120
121int half_precision_decode_basic()
122{
123 UsefulBufC HalfPrecision = UsefulBuf_FromByteArrayLiteral(ExpectedHalf);
124
125 QCBORDecodeContext DC;
126 QCBORDecode_Init(&DC, HalfPrecision, 0);
127
128 QCBORItem Item;
129
130 QCBORDecode_GetNext(&DC, &Item);
131 if(Item.uDataType != QCBOR_TYPE_MAP) {
132 return -1;
133 }
134
135 QCBORDecode_GetNext(&DC, &Item);
136 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0F) {
137 return -1;
138 }
139
140 QCBORDecode_GetNext(&DC, &Item);
141 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != INFINITY) {
142 return -1;
143 }
144
145 QCBORDecode_GetNext(&DC, &Item);
146 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != -INFINITY) {
147 return -1;
148 }
149
150 QCBORDecode_GetNext(&DC, &Item); // TODO, is this really converting right? It is carrying payload, but this confuses things.
151 if(Item.uDataType != QCBOR_TYPE_FLOAT || !isnan(Item.val.fnum)) {
152 return -1;
153 }
154
155 QCBORDecode_GetNext(&DC, &Item);
156 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 1.0F) {
157 return -1;
158 }
159
160 QCBORDecode_GetNext(&DC, &Item);
161 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.333251953125F) {
162 return -1;
163 }
164
165 QCBORDecode_GetNext(&DC, &Item);
166 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 65504.0F) {
167 return -1;
168 }
169
170 QCBORDecode_GetNext(&DC, &Item);
171 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != INFINITY) {
172 return -1;
173 }
174
175 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
176 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0000000596046448F) {
177 return -1;
178 }
179
180 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
181 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0000609755516F) {
182 return -1;
183 }
184
185 QCBORDecode_GetNext(&DC, &Item); // TODO check this
186 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0000610351563F) {
187 return -1;
188 }
189
190 QCBORDecode_GetNext(&DC, &Item);
191 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0) {
192 return -1;
193 }
194
195 QCBORDecode_GetNext(&DC, &Item);
196 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != -2.0F) {
197 return -1;
198 }
199
200 if(QCBORDecode_Finish(&DC)) {
201 return -1;
202 }
203
204 return 0;
205}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700206
207
208int half_precision_to_float_transitive_test()
209{
210 for(uint32_t uHalfP = 0; uHalfP < 0xffff; uHalfP += 1) {
211 // Contruct the CBOR for the half-precision float by hand
212 UsefulBuf_MakeStackUB(EncodedCBORMem, 3);
213 UsefulOutBuf UOB;
214 UsefulOutBuf_Init(&UOB, EncodedCBORMem);
215
216 const uint8_t uHalfPrecInitialByte = HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5); // 0xf9
217 UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
218 UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
219
220
221 // Now parse the hand-constructed CBOR. This will invoke the conversion to a float
222 QCBORDecodeContext DC;
223 QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
224
225 QCBORItem Item;
226 QCBORDecode_GetNext(&DC, &Item);
227 if(Item.uDataType != QCBOR_TYPE_FLOAT) {
228 return -1;
229 }
230
231 //printf("%04x QCBOR:%15.15f \n", uHalfP,Item.val.fnum);
232
233
234 // Now generate CBOR with the half-precision value. This will invoke the conversion from float to half
235 UsefulBuf_MakeStackUB(OtherEncodedCBORMem, 5);
236 QCBOREncodeContext EC;
237 QCBOREncode_Init(&EC, OtherEncodedCBORMem);
238 QCBOREncode_AddFloatAsHalf(&EC, Item.val.fnum);
239 EncodedCBOR EnCBOR;
240 QCBOREncode_Finish2(&EC, &EnCBOR); // todo check return code
241
242
243 // Finally parse the CBOR by hand to get at half-precision that was actually encoded.
244 UsefulInputBuf UIB;
245 UsefulInputBuf_Init(&UIB, EnCBOR.Bytes);
246 if(UsefulInputBuf_GetByte(&UIB) != uHalfPrecInitialByte) {
247 return -2;
248 }
249 if(UsefulInputBuf_GetUint16(&UIB) != uHalfP) { // the moment of truth did we get back what we started with?
250 return -3;
251 }
252 }
253
254 return 0;
255}
256
257
258int half_precision_to_float_vs_rfc_test()
259{
260 for(uint32_t uHalfP = 0; uHalfP < 0x1fff; uHalfP += 10) {
261 unsigned char x[2];
262 x[1] = uHalfP & 0xff;
263 x[0] = uHalfP >> 8;
264 double d = decode_half(x);
265
266 // Contruct the CBOR for the half-precision float by hand
267 UsefulBuf_MakeStackUB(__xx, 3);
268 UsefulOutBuf UOB;
269 UsefulOutBuf_Init(&UOB, __xx);
270
271 const uint8_t uHalfPrecInitialByte = HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5); // 0xf9
272 UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
273 UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
274
275 // Now parse the hand-constructed CBOR. This will invoke the conversion to a float
276 QCBORDecodeContext DC;
277 QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
278
279 QCBORItem Item;
280
281 QCBORDecode_GetNext(&DC, &Item);
282 if(Item.uDataType != QCBOR_TYPE_FLOAT) {
283 return -1;
284 }
285
286 //printf("%04x QCBOR:%15.15f RFC: %15.15f\n", uHalfP,Item.val.fnum, d );
287
288 if(Item.val.fnum != d) {
289 return -2;
290 }
291 }
292 return 0;
293}
294
295
296/*
297 {"zero": 0.0, "negative zero": -0.0, "infinitity": Infinity, "negative infinitity": -Infinity, "NaN": NaN, "one": 1.0, "one third": 0.333251953125, "largest half-precision": 65504.0, "largest half-precision point one": 65504.1, "too-large half-precision": 65536.0, "smallest subnormal": 5.96046448e-8, "smallest normal": 0.00006103515261202119, "biggest subnormal": 0.00006103515625, "subnormal single": 4.00000646641519e-40, 3: -2.0, "large single exp": 2.5521177519070385e+38, "too-large single exp": 5.104235503814077e+38, "biggest single with prec": 16777216.0, "first single with prec loss": 16777217.0, 1: "fin"}
298
299 */
300static const uint8_t sExpectedSmallest[] = {
301 0xB4, 0x64, 0x7A, 0x65, 0x72, 0x6F, 0xF9, 0x00, 0x00, 0x6D, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x7A, 0x65, 0x72, 0x6F, 0xF9, 0x80, 0x00, 0x6A, 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79, 0xF9, 0x7C, 0x00, 0x73, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79, 0xF9, 0xFC, 0x00, 0x63, 0x4E, 0x61, 0x4E, 0xF9, 0x7E, 0x00, 0x63, 0x6F, 0x6E, 0x65, 0xF9, 0x3C, 0x00, 0x69, 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0xF9, 0x35, 0x55, 0x76, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0xF9, 0x7B, 0xFF, 0x78, 0x20, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, 0x6F, 0x6E, 0x65, 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33, 0x78, 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0xFA, 0x47, 0x80, 0x00, 0x00, 0x72, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xFB, 0x3E, 0x70, 0x00, 0x00, 0x00, 0x1C, 0x5F, 0x68, 0x6F, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xFA, 0x38, 0x7F, 0xFF, 0xFF, 0x71, 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xF9, 0x04, 0x00, 0x70, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0xFB, 0x37, 0xC1, 0x6C, 0x28, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF9, 0xC0, 0x00, 0x70, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x65, 0x78, 0x70, 0xFA, 0x7F, 0x40, 0x00, 0x00, 0x74, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x65, 0x78, 0x70, 0xFB, 0x47, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x18, 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0xFA, 0x4B, 0x80, 0x00, 0x00, 0x78, 0x1B, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x20, 0x6C, 0x6F, 0x73, 0x73, 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x63, 0x66, 0x69, 0x6E
302};
303
304
305int double_as_smallest_encode_basic()
306{
307 UsefulBuf_MakeStackUB(EncodedHalfsMem, 420);
308
309 QCBOREncodeContext EC;
310 QCBOREncode_Init(&EC, EncodedHalfsMem);
311 // These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
312 QCBOREncode_OpenMap(&EC);
313 // 64 # text(4)
314 // 7A65726F # "zero"
315 // F9 0000 # primitive(0)
316 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "zero", 0.00);
317
318 // 64 # text(4)
319 // 7A65726F # "negative zero"
320 // F9 8000 # primitive(0)
321 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative zero", -0.00);
322
323 // 6A # text(10)
324 // 696E66696E6974697479 # "infinitity"
325 // F9 7C00 # primitive(31744)
326 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "infinitity", INFINITY);
327
328 // 73 # text(19)
329 // 6E6567617469766520696E66696E6974697479 # "negative infinitity"
330 // F9 FC00 # primitive(64512)
331 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative infinitity", -INFINITY);
332
333 // 63 # text(3)
334 // 4E614E # "NaN"
335 // F9 7E00 # primitive(32256)
336 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "NaN", NAN);
337
338 // TODO: test a few NaN variants
339
340 // 63 # text(3)
341 // 6F6E65 # "one"
342 // F9 3C00 # primitive(15360)
343 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one", 1.0);
344
345 // 69 # text(9)
346 // 6F6E65207468697264 # "one third"
347 // F9 3555 # primitive(13653)
348 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one third", 0.333251953125);
349
350 // 76 # text(22)
351 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
352 // F9 7BFF # primitive(31743)
353 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision",65504.0);
354
355 // 76 # text(22)
356 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
357 // F9 7BFF # primitive(31743)
358 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision point one",65504.1);
359
360 // Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which is larger than 15, the largest half-precision exponent
361 // 78 18 # text(24)
362 // 746F6F2D6C617267652068616C662D707265636973696F6E # "too-large half-precision"
363 // FA 47800000 # primitive(31743)
364 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large half-precision", 65536.0);
365
366 // The smallest possible half-precision subnormal, but digitis are lost converting
367 // to half, so this turns into a double
368 // 72 # text(18)
369 // 736D616C6C657374207375626E6F726D616C # "smallest subnormal"
370 // FB 3E700000001C5F68 # primitive(4499096027744984936)
371 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest subnormal", 0.0000000596046448);
372
373 // The smallest possible half-precision snormal, but digitis are lost converting
374 // to half, so this turns into a single TODO: confirm this is right
375 // 6F # text(15)
376 // 736D616C6C657374206E6F726D616C # "smallest normal"
377 // FA 387FFFFF # primitive(947912703)
378 // in hex single is 0x387fffff, exponent -15, significand 7fffff
379 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest normal", 0.0000610351526F);
380
381 // 71 # text(17)
382 // 62696767657374207375626E6F726D616C # "biggest subnormal"
383 // F9 0400 # primitive(1024)
384 // in hex single is 0x38800000, exponent -14, significand 0
385 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest subnormal", 0.0000610351563F);
386
387 // 70 # text(16)
388 // 7375626E6F726D616C2073696E676C65 # "subnormal single"
389 // FB 37C16C2800000000 # primitive(4017611261645684736)
390 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "subnormal single", 4e-40F);
391
392 // 03 # unsigned(3)
393 // F9 C000 # primitive(49152)
394 QCBOREncode_AddDoubleAsSmallestToMapN(&EC, 3, -2.0);
395
396 // 70 # text(16)
397 // 6C617267652073696E676C6520657870 # "large single exp"
398 // FA 7F400000 # primitive(2134900736)
399 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((127LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
400 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "large single exp", 2.5521177519070385E+38); // Exponent fits single
401
402 // 74 # text(20)
403 // 746F6F2D6C617267652073696E676C6520657870 # "too-large single exp"
404 // FB 47F8000000000000 # primitive(5185894970917126144)
405 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((128LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
406 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large single exp", 5.104235503814077E+38); // Exponent too large for single
407
408 // 66 # text(6)
409 // 646664666465 # "dfdfde"
410 // FA 4B800000 # primitive(1266679808)
411 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest single with prec",16777216); // Single with no precision loss
412
413 // 78 18 # text(24)
414 // 626967676573742073696E676C6520776974682070726563 # "biggest single with prec"
415 // FA 4B800000 # primitive(1266679808)
416 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "first single with prec loss",16777217); // Double becuase of precision loss
417
418 // Just a convenient marker when cutting and pasting encoded CBOR
419 QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
420
421 QCBOREncode_CloseMap(&EC);
422
423 EncodedCBOR EncodedHalfs;
424 int nReturn = QCBOREncode_Finish2(&EC, &EncodedHalfs);
425 if(nReturn) {
426 return -1;
427 }
428
429 if(UsefulBuf_Compare(EncodedHalfs.Bytes, UsefulBuf_FromByteArrayLiteral(sExpectedSmallest))) {
430 return -3;
431 }
432
433 return 0;
434}
435
436
437
438