blob: 936d70f097bea5240e3049b07076daa955162447 [file] [log] [blame]
Laurence Lundblade68a13352018-09-23 02:19:54 -07001/*==============================================================================
Laurence Lundblade781fd822018-10-01 09:37:52 -07002 half_precision_test.c -- tests for converstion to/from half-precision
3
Laurence Lundblade68a13352018-09-23 02:19:54 -07004 Copyright 2018 Laurence Lundblade
5
6 Permission is hereby granted, free of charge, to any person obtaining
7 a copy of this software and associated documentation files (the
8 "Software"), to deal in the Software without restriction, including
9 without limitation the rights to use, copy, modify, merge, publish,
10 distribute, sublicense, and/or sell copies of the Software, and to
11 permit persons to whom the Software is furnished to do so, subject to
12 the following conditions:
13
14 The above copyright notice and this permission notice shall be included
15 in all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25
26 (This is the MIT license)
27 ==============================================================================*/
Laurence Lundblade68a13352018-09-23 02:19:54 -070028// Created by Laurence Lundblade on 9/19/18.
Laurence Lundblade781fd822018-10-01 09:37:52 -070029
Laurence Lundblade68a13352018-09-23 02:19:54 -070030
31#include "half_precision_test.h"
32#include "qcbor.h"
Laurence Lundbladed711fb22018-09-26 14:35:22 -070033#include "half_to_double_from_rfc7049.h"
34#include <math.h> // For INFINITY and NAN and isnan()
Laurence Lundblade68a13352018-09-23 02:19:54 -070035
36static const uint8_t ExpectedHalf[] = {
Laurence Lundblade7d40d812018-09-30 02:44:01 -070037 0xB1,
Laurence Lundblade68a13352018-09-23 02:19:54 -070038 0x64,
39 0x7A, 0x65, 0x72, 0x6F,
40 0xF9, 0x00, 0x00, // 0.000
41 0x6A,
42 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
43 0xF9, 0x7C, 0x00, // Infinity
44 0x73,
45 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
46 0xF9, 0xFC, 0x00, // -Inifinity
47 0x63,
48 0x4E, 0x61, 0x4E,
49 0xF9, 0x7E, 0x00, // NaN
50 0x63,
51 0x6F, 0x6E, 0x65,
52 0xF9, 0x3C, 0x00, // 1.0
53 0x69,
54 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
55 0xF9, 0x35, 0x55, // 0.333251953125
56 0x76,
57 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
58 0xF9, 0x7B, 0xFF, // 65504.0
59 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,
60 0xF9, 0x7C, 0x00, // Infinity
61 0x72,
62 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
63 0xF9, 0x00, 0x01, // 0.000000059604
64 0x6F,
65 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
66 0xF9, 0x03, 0xFF, // 0.0000609755516
67 0x71,
68 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
69 0xF9, 0x04, 0x00, // 0.000061988
70 0x70,
71 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65,
72 0xF9, 0x00, 0x00,
73 0x03,
Laurence Lundblade7d40d812018-09-30 02:44:01 -070074 0xF9, 0xC0, 0x00, // -2
75 0x04,
76 0xF9, 0x7E, 0x00, // qNaN
77 0x05,
78 0xF9, 0x7C, 0x01, // sNaN
79 0x06,
80 0xF9, 0x7E, 0x0F, // qNaN with payload 0x0f
81 0x07,
82 0xF9, 0x7C, 0x0F, // sNaN with payload 0x0f
83
Laurence Lundblade68a13352018-09-23 02:19:54 -070084};
85
86
87
88int half_precision_encode_basic()
89{
Laurence Lundblade7d40d812018-09-30 02:44:01 -070090 UsefulBuf_MakeStackUB(EncodedHalfsMem, 250);
Laurence Lundblade68a13352018-09-23 02:19:54 -070091
92 QCBOREncodeContext EC;
93 QCBOREncode_Init(&EC, EncodedHalfsMem);
94 // These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
95 QCBOREncode_OpenMap(&EC);
96 QCBOREncode_AddFloatAsHalfToMap(&EC, "zero", 0.00F);
97 QCBOREncode_AddFloatAsHalfToMap(&EC, "infinitity", INFINITY);
98 QCBOREncode_AddFloatAsHalfToMap(&EC, "negative infinitity", -INFINITY);
99 QCBOREncode_AddFloatAsHalfToMap(&EC, "NaN", NAN);
100 QCBOREncode_AddFloatAsHalfToMap(&EC, "one", 1.0F);
101 QCBOREncode_AddFloatAsHalfToMap(&EC, "one third", 0.333251953125F);
102 QCBOREncode_AddFloatAsHalfToMap(&EC, "largest half-precision",65504.0F);
103 // Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which is larger than 15, the largest half-precision exponent
104 QCBOREncode_AddFloatAsHalfToMap(&EC, "too-large half-precision", 65536.0F);
105 // Should convert to smallest possible half precision which is encodded as 0x00 0x01 or 5.960464477539063e-8
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700106 QCBOREncode_AddFloatAsHalfToMap(&EC, "smallest subnormal", 0.0000000596046448F);
Laurence Lundblade68a13352018-09-23 02:19:54 -0700107 QCBOREncode_AddFloatAsHalfToMap(&EC, "smallest normal", 0.0000610351526F); // in hex single is 0x387fffff, exponent -15, significand 7fffff
108 QCBOREncode_AddFloatAsHalfToMap(&EC, "biggest subnormal", 0.0000610351563F); // in hex single is 0x38800000, exponent -14, significand 0
109 QCBOREncode_AddFloatAsHalfToMap(&EC, "subnormal single", 4e-40F);
110 QCBOREncode_AddFloatAsHalfToMapN(&EC, 3, -2.0F);
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700111 QCBOREncode_AddFloatAsHalfToMapN(&EC, 4, UsefulBufUtil_CopyUint32ToFloat(0x7fc00000L)); // qNaN
112 QCBOREncode_AddFloatAsHalfToMapN(&EC, 5, UsefulBufUtil_CopyUint32ToFloat(0x7f800001L)); // sNaN
113 QCBOREncode_AddFloatAsHalfToMapN(&EC, 6, UsefulBufUtil_CopyUint32ToFloat(0x7fc0f00fL)); // qNaN with payload
114 QCBOREncode_AddFloatAsHalfToMapN(&EC, 7, UsefulBufUtil_CopyUint32ToFloat(0x7f80f00fL)); // sNaN with payload
Laurence Lundblade68a13352018-09-23 02:19:54 -0700115 QCBOREncode_CloseMap(&EC);
116
Laurence Lundblade781fd822018-10-01 09:37:52 -0700117 UsefulBufC EncodedHalfs;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700118 int nReturn = QCBOREncode_Finish2(&EC, &EncodedHalfs);
Laurence Lundblade68a13352018-09-23 02:19:54 -0700119 if(nReturn) {
120 return -1;
121 }
122
Laurence Lundblade781fd822018-10-01 09:37:52 -0700123 if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FromByteArrayLiteral(ExpectedHalf))) {
Laurence Lundblade68a13352018-09-23 02:19:54 -0700124 return -3;
125 }
126
127 return 0;
128}
129
130
131int half_precision_decode_basic()
132{
133 UsefulBufC HalfPrecision = UsefulBuf_FromByteArrayLiteral(ExpectedHalf);
134
135 QCBORDecodeContext DC;
136 QCBORDecode_Init(&DC, HalfPrecision, 0);
137
138 QCBORItem Item;
139
140 QCBORDecode_GetNext(&DC, &Item);
141 if(Item.uDataType != QCBOR_TYPE_MAP) {
142 return -1;
143 }
144
145 QCBORDecode_GetNext(&DC, &Item);
146 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700147 return -2;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700148 }
149
150 QCBORDecode_GetNext(&DC, &Item);
151 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700152 return -3;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700153 }
154
155 QCBORDecode_GetNext(&DC, &Item);
156 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != -INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700157 return -4;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700158 }
159
160 QCBORDecode_GetNext(&DC, &Item); // TODO, is this really converting right? It is carrying payload, but this confuses things.
161 if(Item.uDataType != QCBOR_TYPE_FLOAT || !isnan(Item.val.fnum)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700162 return -5;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700163 }
164
165 QCBORDecode_GetNext(&DC, &Item);
166 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 1.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700167 return -6;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700168 }
169
170 QCBORDecode_GetNext(&DC, &Item);
171 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.333251953125F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700172 return -7;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700173 }
174
175 QCBORDecode_GetNext(&DC, &Item);
176 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 65504.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700177 return -8;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700178 }
179
180 QCBORDecode_GetNext(&DC, &Item);
181 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700182 return -9;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700183 }
184
185 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
186 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0000000596046448F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700187 return -10;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700188 }
189
190 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
191 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0000609755516F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700192 return -11;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700193 }
194
195 QCBORDecode_GetNext(&DC, &Item); // TODO check this
196 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0000610351563F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700197 return -12;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700198 }
199
200 QCBORDecode_GetNext(&DC, &Item);
201 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700202 return -13;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700203 }
204
205 QCBORDecode_GetNext(&DC, &Item);
206 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != -2.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700207 return -14;
208 }
209
210 QCBORDecode_GetNext(&DC, &Item);
211 if(Item.uDataType != QCBOR_TYPE_FLOAT || UsefulBufUtil_CopyFloatToUint32(Item.val.fnum) != 0x7fc00000L) {
212 return -15;
213 }
214 QCBORDecode_GetNext(&DC, &Item);
215 if(Item.uDataType != QCBOR_TYPE_FLOAT || UsefulBufUtil_CopyFloatToUint32(Item.val.fnum) != 0x7f800001) {
216 return -16;
217 }
218 QCBORDecode_GetNext(&DC, &Item);
219 if(Item.uDataType != QCBOR_TYPE_FLOAT || UsefulBufUtil_CopyFloatToUint32(Item.val.fnum) != 0x7fc0000f) {
220 return -17;
221 }
222 QCBORDecode_GetNext(&DC, &Item);
223 if(Item.uDataType != QCBOR_TYPE_FLOAT || UsefulBufUtil_CopyFloatToUint32(Item.val.fnum) != 0x7f80000f) {
224 return -18;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700225 }
226
227 if(QCBORDecode_Finish(&DC)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700228 return -19;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700229 }
230
231 return 0;
232}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700233
234
235int half_precision_to_float_transitive_test()
236{
237 for(uint32_t uHalfP = 0; uHalfP < 0xffff; uHalfP += 1) {
238 // Contruct the CBOR for the half-precision float by hand
239 UsefulBuf_MakeStackUB(EncodedCBORMem, 3);
240 UsefulOutBuf UOB;
241 UsefulOutBuf_Init(&UOB, EncodedCBORMem);
242
243 const uint8_t uHalfPrecInitialByte = HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5); // 0xf9
244 UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
245 UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
246
247
248 // Now parse the hand-constructed CBOR. This will invoke the conversion to a float
249 QCBORDecodeContext DC;
250 QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
251
252 QCBORItem Item;
253 QCBORDecode_GetNext(&DC, &Item);
254 if(Item.uDataType != QCBOR_TYPE_FLOAT) {
255 return -1;
256 }
257
258 //printf("%04x QCBOR:%15.15f \n", uHalfP,Item.val.fnum);
259
260
261 // Now generate CBOR with the half-precision value. This will invoke the conversion from float to half
262 UsefulBuf_MakeStackUB(OtherEncodedCBORMem, 5);
263 QCBOREncodeContext EC;
264 QCBOREncode_Init(&EC, OtherEncodedCBORMem);
265 QCBOREncode_AddFloatAsHalf(&EC, Item.val.fnum);
Laurence Lundblade781fd822018-10-01 09:37:52 -0700266 UsefulBufC EnCBOR;
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700267 QCBOREncode_Finish2(&EC, &EnCBOR); // todo check return code
268
269
270 // Finally parse the CBOR by hand to get at half-precision that was actually encoded.
271 UsefulInputBuf UIB;
Laurence Lundblade781fd822018-10-01 09:37:52 -0700272 UsefulInputBuf_Init(&UIB, EnCBOR);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700273 if(UsefulInputBuf_GetByte(&UIB) != uHalfPrecInitialByte) {
274 return -2;
275 }
276 if(UsefulInputBuf_GetUint16(&UIB) != uHalfP) { // the moment of truth did we get back what we started with?
277 return -3;
278 }
279 }
280
281 return 0;
282}
283
284
285int half_precision_to_float_vs_rfc_test()
286{
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700287 for(uint32_t uHalfP = 0; uHalfP < 0xffff; uHalfP += 60) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700288 unsigned char x[2];
289 x[1] = uHalfP & 0xff;
290 x[0] = uHalfP >> 8;
291 double d = decode_half(x);
292
293 // Contruct the CBOR for the half-precision float by hand
294 UsefulBuf_MakeStackUB(__xx, 3);
295 UsefulOutBuf UOB;
296 UsefulOutBuf_Init(&UOB, __xx);
297
298 const uint8_t uHalfPrecInitialByte = HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5); // 0xf9
299 UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
300 UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
301
302 // Now parse the hand-constructed CBOR. This will invoke the conversion to a float
303 QCBORDecodeContext DC;
304 QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
305
306 QCBORItem Item;
307
308 QCBORDecode_GetNext(&DC, &Item);
309 if(Item.uDataType != QCBOR_TYPE_FLOAT) {
310 return -1;
311 }
312
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700313 //printf("%04x QCBOR:%15.15f RFC: %15.15f (%8x)\n", uHalfP,Item.val.fnum, d , UsefulBufUtil_CopyFloatToUint32(d));
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700314
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700315 if(isnan(d)) {
316 // The RFC code uses the native instructions which may or may not
317 // handle sNaN, qNaN and NaN payloads correctly. This test just
318 // makes sure it is a NaN and doesn't worry about the type of NaN
319 if(!isnan(Item.val.fnum)) {
320 return -3;
321 }
322 } else {
323 if(Item.val.fnum != d) {
324 return -2;
325 }
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700326 }
327 }
328 return 0;
329}
330
331
332/*
333 {"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"}
334
335 */
336static const uint8_t sExpectedSmallest[] = {
337 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
338};
339
340
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700341
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700342int double_as_smallest_encode_basic()
343{
344 UsefulBuf_MakeStackUB(EncodedHalfsMem, 420);
345
346 QCBOREncodeContext EC;
347 QCBOREncode_Init(&EC, EncodedHalfsMem);
348 // These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
349 QCBOREncode_OpenMap(&EC);
350 // 64 # text(4)
351 // 7A65726F # "zero"
352 // F9 0000 # primitive(0)
353 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "zero", 0.00);
354
355 // 64 # text(4)
356 // 7A65726F # "negative zero"
357 // F9 8000 # primitive(0)
358 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative zero", -0.00);
359
360 // 6A # text(10)
361 // 696E66696E6974697479 # "infinitity"
362 // F9 7C00 # primitive(31744)
363 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "infinitity", INFINITY);
364
365 // 73 # text(19)
366 // 6E6567617469766520696E66696E6974697479 # "negative infinitity"
367 // F9 FC00 # primitive(64512)
368 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative infinitity", -INFINITY);
369
370 // 63 # text(3)
371 // 4E614E # "NaN"
372 // F9 7E00 # primitive(32256)
373 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "NaN", NAN);
374
375 // TODO: test a few NaN variants
376
377 // 63 # text(3)
378 // 6F6E65 # "one"
379 // F9 3C00 # primitive(15360)
380 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one", 1.0);
381
382 // 69 # text(9)
383 // 6F6E65207468697264 # "one third"
384 // F9 3555 # primitive(13653)
385 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one third", 0.333251953125);
386
387 // 76 # text(22)
388 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
389 // F9 7BFF # primitive(31743)
390 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision",65504.0);
391
392 // 76 # text(22)
393 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
394 // F9 7BFF # primitive(31743)
395 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision point one",65504.1);
396
397 // Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which is larger than 15, the largest half-precision exponent
398 // 78 18 # text(24)
399 // 746F6F2D6C617267652068616C662D707265636973696F6E # "too-large half-precision"
400 // FA 47800000 # primitive(31743)
401 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large half-precision", 65536.0);
402
403 // The smallest possible half-precision subnormal, but digitis are lost converting
404 // to half, so this turns into a double
405 // 72 # text(18)
406 // 736D616C6C657374207375626E6F726D616C # "smallest subnormal"
407 // FB 3E700000001C5F68 # primitive(4499096027744984936)
408 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest subnormal", 0.0000000596046448);
409
410 // The smallest possible half-precision snormal, but digitis are lost converting
411 // to half, so this turns into a single TODO: confirm this is right
412 // 6F # text(15)
413 // 736D616C6C657374206E6F726D616C # "smallest normal"
414 // FA 387FFFFF # primitive(947912703)
415 // in hex single is 0x387fffff, exponent -15, significand 7fffff
416 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest normal", 0.0000610351526F);
417
418 // 71 # text(17)
419 // 62696767657374207375626E6F726D616C # "biggest subnormal"
420 // F9 0400 # primitive(1024)
421 // in hex single is 0x38800000, exponent -14, significand 0
422 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest subnormal", 0.0000610351563F);
423
424 // 70 # text(16)
425 // 7375626E6F726D616C2073696E676C65 # "subnormal single"
426 // FB 37C16C2800000000 # primitive(4017611261645684736)
427 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "subnormal single", 4e-40F);
428
429 // 03 # unsigned(3)
430 // F9 C000 # primitive(49152)
431 QCBOREncode_AddDoubleAsSmallestToMapN(&EC, 3, -2.0);
432
433 // 70 # text(16)
434 // 6C617267652073696E676C6520657870 # "large single exp"
435 // FA 7F400000 # primitive(2134900736)
436 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((127LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
437 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "large single exp", 2.5521177519070385E+38); // Exponent fits single
438
439 // 74 # text(20)
440 // 746F6F2D6C617267652073696E676C6520657870 # "too-large single exp"
441 // FB 47F8000000000000 # primitive(5185894970917126144)
442 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((128LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
443 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large single exp", 5.104235503814077E+38); // Exponent too large for single
444
445 // 66 # text(6)
446 // 646664666465 # "dfdfde"
447 // FA 4B800000 # primitive(1266679808)
448 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest single with prec",16777216); // Single with no precision loss
449
450 // 78 18 # text(24)
451 // 626967676573742073696E676C6520776974682070726563 # "biggest single with prec"
452 // FA 4B800000 # primitive(1266679808)
453 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "first single with prec loss",16777217); // Double becuase of precision loss
454
455 // Just a convenient marker when cutting and pasting encoded CBOR
456 QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
457
458 QCBOREncode_CloseMap(&EC);
459
Laurence Lundblade781fd822018-10-01 09:37:52 -0700460 UsefulBufC EncodedHalfs;
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700461 int nReturn = QCBOREncode_Finish2(&EC, &EncodedHalfs);
462 if(nReturn) {
463 return -1;
464 }
465
Laurence Lundblade781fd822018-10-01 09:37:52 -0700466 if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FromByteArrayLiteral(sExpectedSmallest))) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700467 return -3;
468 }
469
470 return 0;
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700471};
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700472
473
474
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700475#ifdef NAN_EXPERIMENT
476/*
477 Code for checking what the double to float cast does with
478 NaNs. Not run as part of tests. Keep it around to
479 be able to check various platforms and CPUs.
480 */
481
482#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
483#define DOUBLE_NUM_EXPONENT_BITS (11)
484#define DOUBLE_NUM_SIGN_BITS (1)
485
486#define DOUBLE_SIGNIFICAND_SHIFT (0)
487#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
488#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
489
490#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
491#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
492#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
493#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
494
495
496static int NaNExperiments() {
497 double dqNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT);
498 double dsNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | 0x01);
499 double dqNaNPayload = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT | 0xf00f);
500
501 float f1 = (float)dqNaN;
502 float f2 = (float)dsNaN;
503 float f3 = (float)dqNaNPayload;
504
505
506 uint32_t uqNaN = UsefulBufUtil_CopyFloatToUint32((float)dqNaN);
507 uint32_t usNaN = UsefulBufUtil_CopyFloatToUint32((float)dsNaN);
508 uint32_t uqNaNPayload = UsefulBufUtil_CopyFloatToUint32((float)dqNaNPayload);
509
510 // Result of this on x86 is that every NaN is a qNaN. The intel
511 // CVTSD2SS instruction ignores the NaN payload and even converts
512 // a sNaN to a qNaN.
513
514 return 0;
515}
516#endif
517
518
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700519