blob: 6486d02264ea9f4e8c5a099116ea8571dbc96150 [file] [log] [blame]
Laurence Lundblade68a13352018-09-23 02:19:54 -07001/*==============================================================================
Laurence Lundblade2d85ce42018-10-12 14:12:47 +08002 float_tests.c -- tests for float and conversion to/from half-precision
Laurence Lundblade781fd822018-10-01 09:37:52 -07003
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
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080031#include "float_tests.h"
Laurence Lundblade68a13352018-09-23 02:19:54 -070032#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
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080036
Laurence Lundbladebb474be2018-10-22 11:53:21 +053037/*
38 Output from http://cbor.me
39 [0.0,
40 1.0,
41 1.100000023841858,
42 1.5,
43 65504.0,
44 100000.0,
45 3.4028234663852886e+38,
46 Infinity,
47 5.960464477539063e-8,
48 0.00006103515625,
49 -4.0,
50 -4.099999904632568,
51 NaN,
52 Infinity,
53 -Infinity,
54 0.0,
55 1.0,
56 1.1,
57 1.5,
58 65504.0,
59 100000.0,
60 3.4028234663852886e+38,
61 1.0e+300,
62 5.960464477539063e-8,
63 0.00006103515625,
64 -4.0,
65 -4.1,
66 NaN,
67 Infinity,
68 -Infinity]
69 */
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080070
Laurence Lundbladebb474be2018-10-22 11:53:21 +053071static uint8_t spExpectedEncodedFloat[] = {
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080072 0x98, 0x1e, 0xfa, 0x00, 0x00, 0x00, 0x00, 0xfa,
73 0x3f, 0x80, 0x00, 0x00, 0xfa, 0x3f, 0x8c, 0xcc,
74 0xcd, 0xfa, 0x3f, 0xc0, 0x00, 0x00, 0xfa, 0x47,
75 0x7f, 0xe0, 0x00, 0xfa, 0x47, 0xc3, 0x50, 0x00,
76 0xfa, 0x7f, 0x7f, 0xff, 0xff, 0xfa, 0x7f, 0x80,
77 0x00, 0x00, 0xfa, 0x33, 0x80, 0x00, 0x00, 0xfa,
78 0x38, 0x80, 0x00, 0x00, 0xfa, 0xc0, 0x80, 0x00,
79 0x00, 0xfa, 0xc0, 0x83, 0x33, 0x33, 0xfa, 0x7f,
80 0xc0, 0x00, 0x00, 0xfa, 0x7f, 0x80, 0x00, 0x00,
81 0xfa, 0xff, 0x80, 0x00, 0x00, 0xfb, 0x00, 0x00,
82 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x3f,
83 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb,
84 0x3f, 0xf1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a,
85 0xfb, 0x3f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
86 0x00, 0xfb, 0x40, 0xef, 0xfc, 0x00, 0x00, 0x00,
87 0x00, 0x00, 0xfb, 0x40, 0xf8, 0x6a, 0x00, 0x00,
88 0x00, 0x00, 0x00, 0xfb, 0x47, 0xef, 0xff, 0xff,
89 0xe0, 0x00, 0x00, 0x00, 0xfb, 0x7e, 0x37, 0xe4,
90 0x3c, 0x88, 0x00, 0x75, 0x9c, 0xfb, 0x3e, 0x70,
91 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x3f,
92 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb,
93 0xc0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
94 0xfb, 0xc0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66,
95 0x66, 0xfb, 0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00,
96 0x00, 0x00, 0xfb, 0x7f, 0xf0, 0x00, 0x00, 0x00,
97 0x00, 0x00, 0x00, 0xfb, 0xff, 0xf0, 0x00, 0x00,
98 0x00, 0x00, 0x00, 0x00};
99
100
101int FloatValuesTest1()
102{
103 QCBOREncodeContext ECtx;
104 int nReturn = 0;
105
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530106 UsefulBuf_MAKE_STACK_UB(EncodedStorage, 220);
Laurence Lundblade2d85ce42018-10-12 14:12:47 +0800107
108 QCBOREncode_Init(&ECtx, EncodedStorage);
109 QCBOREncode_OpenArray(&ECtx);
110
111 // These are all samples published
112 // in RFC 7049.
113 QCBOREncode_AddFloat(&ECtx, 0.0);
114 QCBOREncode_AddFloat(&ECtx, 1.0);
115 QCBOREncode_AddFloat(&ECtx, 1.1); // appx
116 QCBOREncode_AddFloat(&ECtx, 1.5);
117 QCBOREncode_AddFloat(&ECtx, 65504.0);
118 QCBOREncode_AddFloat(&ECtx, 100000.0);
119 QCBOREncode_AddFloat(&ECtx, 3.4028234663852886e+38);
120 QCBOREncode_AddFloat(&ECtx, 1.0e+300); // Infinity?
121 QCBOREncode_AddFloat(&ECtx, 5.960464477539063e-8);
122 QCBOREncode_AddFloat(&ECtx, 0.00006103515625);
123 QCBOREncode_AddFloat(&ECtx, -4.0);
124 QCBOREncode_AddFloat(&ECtx, -4.1); // appx
125
126 QCBOREncode_AddFloat(&ECtx, NAN);
127 QCBOREncode_AddFloat(&ECtx, INFINITY);
128 QCBOREncode_AddFloat(&ECtx, -INFINITY);
129
130
131 QCBOREncode_AddDouble(&ECtx, 0.0);
132 QCBOREncode_AddDouble(&ECtx, 1.0);
133 QCBOREncode_AddDouble(&ECtx, 1.1); // appx
134 QCBOREncode_AddDouble(&ECtx, 1.5);
135 QCBOREncode_AddDouble(&ECtx, 65504.0);
136 QCBOREncode_AddDouble(&ECtx, 100000.0);
137 QCBOREncode_AddDouble(&ECtx, 3.4028234663852886e+38);
138 QCBOREncode_AddDouble(&ECtx, 1.0e+300); // Infinity?
139 QCBOREncode_AddDouble(&ECtx, 5.960464477539063e-8);
140 QCBOREncode_AddDouble(&ECtx, 0.00006103515625);
141 QCBOREncode_AddDouble(&ECtx, -4.0);
142 QCBOREncode_AddDouble(&ECtx, -4.1); // appx
143
144 QCBOREncode_AddDouble(&ECtx, NAN);
145 QCBOREncode_AddDouble(&ECtx, INFINITY);
146 QCBOREncode_AddDouble(&ECtx, -INFINITY);
147
148 QCBOREncode_CloseArray(&ECtx);
149
150 UsefulBufC Encoded;
151 if(QCBOREncode_Finish2(&ECtx, &Encoded)) {
152 nReturn = -1;
153 }
154
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530155 if(UsefulBuf_Compare(Encoded, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedEncodedFloat))) {
Laurence Lundblade2d85ce42018-10-12 14:12:47 +0800156 nReturn = -2;
157 }
158
159 //printencoded(pEncoded, nEncodedLen);
160
161 return(nReturn);
162}
163
164
165
166
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530167static const uint8_t spExpectedHalf[] = {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700168 0xB1,
Laurence Lundblade68a13352018-09-23 02:19:54 -0700169 0x64,
170 0x7A, 0x65, 0x72, 0x6F,
171 0xF9, 0x00, 0x00, // 0.000
172 0x6A,
173 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
174 0xF9, 0x7C, 0x00, // Infinity
175 0x73,
176 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
177 0xF9, 0xFC, 0x00, // -Inifinity
178 0x63,
179 0x4E, 0x61, 0x4E,
180 0xF9, 0x7E, 0x00, // NaN
181 0x63,
182 0x6F, 0x6E, 0x65,
183 0xF9, 0x3C, 0x00, // 1.0
184 0x69,
185 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
186 0xF9, 0x35, 0x55, // 0.333251953125
187 0x76,
188 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
189 0xF9, 0x7B, 0xFF, // 65504.0
190 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,
191 0xF9, 0x7C, 0x00, // Infinity
192 0x72,
193 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
194 0xF9, 0x00, 0x01, // 0.000000059604
195 0x6F,
196 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
197 0xF9, 0x03, 0xFF, // 0.0000609755516
198 0x71,
199 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
200 0xF9, 0x04, 0x00, // 0.000061988
201 0x70,
202 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65,
203 0xF9, 0x00, 0x00,
204 0x03,
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700205 0xF9, 0xC0, 0x00, // -2
206 0x04,
207 0xF9, 0x7E, 0x00, // qNaN
208 0x05,
209 0xF9, 0x7C, 0x01, // sNaN
210 0x06,
211 0xF9, 0x7E, 0x0F, // qNaN with payload 0x0f
212 0x07,
213 0xF9, 0x7C, 0x0F, // sNaN with payload 0x0f
214
Laurence Lundblade68a13352018-09-23 02:19:54 -0700215};
216
217
218
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530219int HalfPrecisionEncodeBasicTests()
Laurence Lundblade68a13352018-09-23 02:19:54 -0700220{
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530221 UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, 250);
Laurence Lundblade68a13352018-09-23 02:19:54 -0700222
223 QCBOREncodeContext EC;
224 QCBOREncode_Init(&EC, EncodedHalfsMem);
225 // These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
226 QCBOREncode_OpenMap(&EC);
227 QCBOREncode_AddFloatAsHalfToMap(&EC, "zero", 0.00F);
228 QCBOREncode_AddFloatAsHalfToMap(&EC, "infinitity", INFINITY);
229 QCBOREncode_AddFloatAsHalfToMap(&EC, "negative infinitity", -INFINITY);
230 QCBOREncode_AddFloatAsHalfToMap(&EC, "NaN", NAN);
231 QCBOREncode_AddFloatAsHalfToMap(&EC, "one", 1.0F);
232 QCBOREncode_AddFloatAsHalfToMap(&EC, "one third", 0.333251953125F);
233 QCBOREncode_AddFloatAsHalfToMap(&EC, "largest half-precision",65504.0F);
234 // Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which is larger than 15, the largest half-precision exponent
235 QCBOREncode_AddFloatAsHalfToMap(&EC, "too-large half-precision", 65536.0F);
236 // Should convert to smallest possible half precision which is encodded as 0x00 0x01 or 5.960464477539063e-8
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700237 QCBOREncode_AddFloatAsHalfToMap(&EC, "smallest subnormal", 0.0000000596046448F);
Laurence Lundblade68a13352018-09-23 02:19:54 -0700238 QCBOREncode_AddFloatAsHalfToMap(&EC, "smallest normal", 0.0000610351526F); // in hex single is 0x387fffff, exponent -15, significand 7fffff
239 QCBOREncode_AddFloatAsHalfToMap(&EC, "biggest subnormal", 0.0000610351563F); // in hex single is 0x38800000, exponent -14, significand 0
240 QCBOREncode_AddFloatAsHalfToMap(&EC, "subnormal single", 4e-40F);
241 QCBOREncode_AddFloatAsHalfToMapN(&EC, 3, -2.0F);
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700242 QCBOREncode_AddFloatAsHalfToMapN(&EC, 4, UsefulBufUtil_CopyUint32ToFloat(0x7fc00000L)); // qNaN
243 QCBOREncode_AddFloatAsHalfToMapN(&EC, 5, UsefulBufUtil_CopyUint32ToFloat(0x7f800001L)); // sNaN
244 QCBOREncode_AddFloatAsHalfToMapN(&EC, 6, UsefulBufUtil_CopyUint32ToFloat(0x7fc0f00fL)); // qNaN with payload
245 QCBOREncode_AddFloatAsHalfToMapN(&EC, 7, UsefulBufUtil_CopyUint32ToFloat(0x7f80f00fL)); // sNaN with payload
Laurence Lundblade68a13352018-09-23 02:19:54 -0700246 QCBOREncode_CloseMap(&EC);
247
Laurence Lundblade781fd822018-10-01 09:37:52 -0700248 UsefulBufC EncodedHalfs;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700249 int nReturn = QCBOREncode_Finish2(&EC, &EncodedHalfs);
Laurence Lundblade68a13352018-09-23 02:19:54 -0700250 if(nReturn) {
251 return -1;
252 }
253
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530254 if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedHalf))) {
Laurence Lundblade68a13352018-09-23 02:19:54 -0700255 return -3;
256 }
257
258 return 0;
259}
260
261
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530262int HalfPrecisionDecodeBasicTests()
Laurence Lundblade68a13352018-09-23 02:19:54 -0700263{
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530264 UsefulBufC HalfPrecision = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedHalf);
Laurence Lundblade68a13352018-09-23 02:19:54 -0700265
266 QCBORDecodeContext DC;
267 QCBORDecode_Init(&DC, HalfPrecision, 0);
268
269 QCBORItem Item;
270
271 QCBORDecode_GetNext(&DC, &Item);
272 if(Item.uDataType != QCBOR_TYPE_MAP) {
273 return -1;
274 }
275
276 QCBORDecode_GetNext(&DC, &Item);
277 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700278 return -2;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700279 }
280
281 QCBORDecode_GetNext(&DC, &Item);
282 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700283 return -3;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700284 }
285
286 QCBORDecode_GetNext(&DC, &Item);
287 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != -INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700288 return -4;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700289 }
290
291 QCBORDecode_GetNext(&DC, &Item); // TODO, is this really converting right? It is carrying payload, but this confuses things.
292 if(Item.uDataType != QCBOR_TYPE_FLOAT || !isnan(Item.val.fnum)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700293 return -5;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700294 }
295
296 QCBORDecode_GetNext(&DC, &Item);
297 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 1.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700298 return -6;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700299 }
300
301 QCBORDecode_GetNext(&DC, &Item);
302 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.333251953125F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700303 return -7;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700304 }
305
306 QCBORDecode_GetNext(&DC, &Item);
307 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 65504.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700308 return -8;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700309 }
310
311 QCBORDecode_GetNext(&DC, &Item);
312 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700313 return -9;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700314 }
315
316 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
317 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0000000596046448F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700318 return -10;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700319 }
320
321 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
322 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0000609755516F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700323 return -11;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700324 }
325
326 QCBORDecode_GetNext(&DC, &Item); // TODO check this
327 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0000610351563F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700328 return -12;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700329 }
330
331 QCBORDecode_GetNext(&DC, &Item);
332 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700333 return -13;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700334 }
335
336 QCBORDecode_GetNext(&DC, &Item);
337 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != -2.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700338 return -14;
339 }
340
341 QCBORDecode_GetNext(&DC, &Item);
342 if(Item.uDataType != QCBOR_TYPE_FLOAT || UsefulBufUtil_CopyFloatToUint32(Item.val.fnum) != 0x7fc00000L) {
343 return -15;
344 }
345 QCBORDecode_GetNext(&DC, &Item);
346 if(Item.uDataType != QCBOR_TYPE_FLOAT || UsefulBufUtil_CopyFloatToUint32(Item.val.fnum) != 0x7f800001) {
347 return -16;
348 }
349 QCBORDecode_GetNext(&DC, &Item);
350 if(Item.uDataType != QCBOR_TYPE_FLOAT || UsefulBufUtil_CopyFloatToUint32(Item.val.fnum) != 0x7fc0000f) {
351 return -17;
352 }
353 QCBORDecode_GetNext(&DC, &Item);
354 if(Item.uDataType != QCBOR_TYPE_FLOAT || UsefulBufUtil_CopyFloatToUint32(Item.val.fnum) != 0x7f80000f) {
355 return -18;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700356 }
357
358 if(QCBORDecode_Finish(&DC)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700359 return -19;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700360 }
361
362 return 0;
363}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700364
365
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530366int HalfPrecisionTransitiveTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700367{
368 for(uint32_t uHalfP = 0; uHalfP < 0xffff; uHalfP += 1) {
369 // Contruct the CBOR for the half-precision float by hand
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530370 UsefulBuf_MAKE_STACK_UB(EncodedCBORMem, 3);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700371 UsefulOutBuf UOB;
372 UsefulOutBuf_Init(&UOB, EncodedCBORMem);
373
374 const uint8_t uHalfPrecInitialByte = HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5); // 0xf9
375 UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
376 UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
377
378
379 // Now parse the hand-constructed CBOR. This will invoke the conversion to a float
380 QCBORDecodeContext DC;
381 QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
382
383 QCBORItem Item;
384 QCBORDecode_GetNext(&DC, &Item);
385 if(Item.uDataType != QCBOR_TYPE_FLOAT) {
386 return -1;
387 }
388
389 //printf("%04x QCBOR:%15.15f \n", uHalfP,Item.val.fnum);
390
391
392 // Now generate CBOR with the half-precision value. This will invoke the conversion from float to half
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530393 UsefulBuf_MAKE_STACK_UB(OtherEncodedCBORMem, 5);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700394 QCBOREncodeContext EC;
395 QCBOREncode_Init(&EC, OtherEncodedCBORMem);
396 QCBOREncode_AddFloatAsHalf(&EC, Item.val.fnum);
Laurence Lundblade781fd822018-10-01 09:37:52 -0700397 UsefulBufC EnCBOR;
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700398 QCBOREncode_Finish2(&EC, &EnCBOR); // todo check return code
399
400
401 // Finally parse the CBOR by hand to get at half-precision that was actually encoded.
402 UsefulInputBuf UIB;
Laurence Lundblade781fd822018-10-01 09:37:52 -0700403 UsefulInputBuf_Init(&UIB, EnCBOR);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700404 if(UsefulInputBuf_GetByte(&UIB) != uHalfPrecInitialByte) {
405 return -2;
406 }
407 if(UsefulInputBuf_GetUint16(&UIB) != uHalfP) { // the moment of truth did we get back what we started with?
408 return -3;
409 }
410 }
411
412 return 0;
413}
414
415
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530416int HalfPrecisionAgainstRFCCodeTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700417{
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700418 for(uint32_t uHalfP = 0; uHalfP < 0xffff; uHalfP += 60) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700419 unsigned char x[2];
420 x[1] = uHalfP & 0xff;
421 x[0] = uHalfP >> 8;
422 double d = decode_half(x);
423
424 // Contruct the CBOR for the half-precision float by hand
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530425 UsefulBuf_MAKE_STACK_UB(__xx, 3);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700426 UsefulOutBuf UOB;
427 UsefulOutBuf_Init(&UOB, __xx);
428
429 const uint8_t uHalfPrecInitialByte = HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5); // 0xf9
430 UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
431 UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
432
433 // Now parse the hand-constructed CBOR. This will invoke the conversion to a float
434 QCBORDecodeContext DC;
435 QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
436
437 QCBORItem Item;
438
439 QCBORDecode_GetNext(&DC, &Item);
440 if(Item.uDataType != QCBOR_TYPE_FLOAT) {
441 return -1;
442 }
443
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700444 //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 -0700445
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700446 if(isnan(d)) {
447 // The RFC code uses the native instructions which may or may not
448 // handle sNaN, qNaN and NaN payloads correctly. This test just
449 // makes sure it is a NaN and doesn't worry about the type of NaN
450 if(!isnan(Item.val.fnum)) {
451 return -3;
452 }
453 } else {
454 if(Item.val.fnum != d) {
455 return -2;
456 }
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700457 }
458 }
459 return 0;
460}
461
462
463/*
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530464 {"zero": 0.0,
465 "negative zero": -0.0,
466 "infinitity": Infinity,
467 "negative infinitity": -Infinity,
468 "NaN": NaN,
469 "one": 1.0,
470 "one third": 0.333251953125,
471 "largest half-precision": 65504.0,
472 "largest half-precision point one": 65504.1,
473 "too-large half-precision": 65536.0,
474 "smallest subnormal": 5.96046448e-8,
475 "smallest normal": 0.00006103515261202119,
476 "biggest subnormal": 0.00006103515625,
477 "subnormal single": 4.00000646641519e-40,
478 3: -2.0,
479 "large single exp": 2.5521177519070385e+38,
480 "too-large single exp": 5.104235503814077e+38,
481 "biggest single with prec": 16777216.0,
482 "first single with prec loss": 16777217.0,
483 1: "fin"}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700484 */
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530485static const uint8_t spExpectedSmallest[] = {
486 0xB4, 0x64, 0x7A, 0x65, 0x72, 0x6F, 0xF9, 0x00, 0x00, 0x6D,
487 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x7A,
488 0x65, 0x72, 0x6F, 0xF9, 0x80, 0x00, 0x6A, 0x69, 0x6E, 0x66,
489 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79, 0xF9, 0x7C, 0x00,
490 0x73, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20,
491 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
492 0xF9, 0xFC, 0x00, 0x63, 0x4E, 0x61, 0x4E, 0xF9, 0x7E, 0x00,
493 0x63, 0x6F, 0x6E, 0x65, 0xF9, 0x3C, 0x00, 0x69, 0x6F, 0x6E,
494 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0xF9, 0x35, 0x55,
495 0x76, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68,
496 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73,
497 0x69, 0x6F, 0x6E, 0xF9, 0x7B, 0xFF, 0x78, 0x20, 0x6C, 0x61,
498 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66,
499 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
500 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, 0x6F, 0x6E, 0x65,
501 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33, 0x78,
502 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65,
503 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63,
504 0x69, 0x73, 0x69, 0x6F, 0x6E, 0xFA, 0x47, 0x80, 0x00, 0x00,
505 0x72, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
506 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xFB,
507 0x3E, 0x70, 0x00, 0x00, 0x00, 0x1C, 0x5F, 0x68, 0x6F, 0x73,
508 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
509 0x72, 0x6D, 0x61, 0x6C, 0xFA, 0x38, 0x7F, 0xFF, 0xFF, 0x71,
510 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
511 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xF9, 0x04, 0x00,
512 0x70, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
513 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0xFB, 0x37, 0xC1,
514 0x6C, 0x28, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF9, 0xC0, 0x00,
515 0x70, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E,
516 0x67, 0x6C, 0x65, 0x20, 0x65, 0x78, 0x70, 0xFA, 0x7F, 0x40,
517 0x00, 0x00, 0x74, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72,
518 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20,
519 0x65, 0x78, 0x70, 0xFB, 0x47, 0xF8, 0x00, 0x00, 0x00, 0x00,
520 0x00, 0x00, 0x78, 0x18, 0x62, 0x69, 0x67, 0x67, 0x65, 0x73,
521 0x74, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77,
522 0x69, 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0xFA, 0x4B,
523 0x80, 0x00, 0x00, 0x78, 0x1B, 0x66, 0x69, 0x72, 0x73, 0x74,
524 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77, 0x69,
525 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x20, 0x6C, 0x6F,
526 0x73, 0x73, 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00,
527 0x00, 0x01, 0x63, 0x66, 0x69, 0x6E
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700528};
529
530
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530531int DoubleAsSmallestTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700532{
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530533 UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, 420);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700534
535 QCBOREncodeContext EC;
536 QCBOREncode_Init(&EC, EncodedHalfsMem);
537 // These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
538 QCBOREncode_OpenMap(&EC);
539 // 64 # text(4)
540 // 7A65726F # "zero"
541 // F9 0000 # primitive(0)
542 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "zero", 0.00);
543
544 // 64 # text(4)
545 // 7A65726F # "negative zero"
546 // F9 8000 # primitive(0)
547 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative zero", -0.00);
548
549 // 6A # text(10)
550 // 696E66696E6974697479 # "infinitity"
551 // F9 7C00 # primitive(31744)
552 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "infinitity", INFINITY);
553
554 // 73 # text(19)
555 // 6E6567617469766520696E66696E6974697479 # "negative infinitity"
556 // F9 FC00 # primitive(64512)
557 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative infinitity", -INFINITY);
558
559 // 63 # text(3)
560 // 4E614E # "NaN"
561 // F9 7E00 # primitive(32256)
562 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "NaN", NAN);
563
564 // TODO: test a few NaN variants
565
566 // 63 # text(3)
567 // 6F6E65 # "one"
568 // F9 3C00 # primitive(15360)
569 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one", 1.0);
570
571 // 69 # text(9)
572 // 6F6E65207468697264 # "one third"
573 // F9 3555 # primitive(13653)
574 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one third", 0.333251953125);
575
576 // 76 # text(22)
577 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
578 // F9 7BFF # primitive(31743)
579 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision",65504.0);
580
581 // 76 # text(22)
582 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
583 // F9 7BFF # primitive(31743)
584 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision point one",65504.1);
585
586 // Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which is larger than 15, the largest half-precision exponent
587 // 78 18 # text(24)
588 // 746F6F2D6C617267652068616C662D707265636973696F6E # "too-large half-precision"
589 // FA 47800000 # primitive(31743)
590 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large half-precision", 65536.0);
591
592 // The smallest possible half-precision subnormal, but digitis are lost converting
593 // to half, so this turns into a double
594 // 72 # text(18)
595 // 736D616C6C657374207375626E6F726D616C # "smallest subnormal"
596 // FB 3E700000001C5F68 # primitive(4499096027744984936)
597 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest subnormal", 0.0000000596046448);
598
599 // The smallest possible half-precision snormal, but digitis are lost converting
600 // to half, so this turns into a single TODO: confirm this is right
601 // 6F # text(15)
602 // 736D616C6C657374206E6F726D616C # "smallest normal"
603 // FA 387FFFFF # primitive(947912703)
604 // in hex single is 0x387fffff, exponent -15, significand 7fffff
605 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest normal", 0.0000610351526F);
606
607 // 71 # text(17)
608 // 62696767657374207375626E6F726D616C # "biggest subnormal"
609 // F9 0400 # primitive(1024)
610 // in hex single is 0x38800000, exponent -14, significand 0
611 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest subnormal", 0.0000610351563F);
612
613 // 70 # text(16)
614 // 7375626E6F726D616C2073696E676C65 # "subnormal single"
615 // FB 37C16C2800000000 # primitive(4017611261645684736)
616 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "subnormal single", 4e-40F);
617
618 // 03 # unsigned(3)
619 // F9 C000 # primitive(49152)
620 QCBOREncode_AddDoubleAsSmallestToMapN(&EC, 3, -2.0);
621
622 // 70 # text(16)
623 // 6C617267652073696E676C6520657870 # "large single exp"
624 // FA 7F400000 # primitive(2134900736)
625 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((127LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
626 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "large single exp", 2.5521177519070385E+38); // Exponent fits single
627
628 // 74 # text(20)
629 // 746F6F2D6C617267652073696E676C6520657870 # "too-large single exp"
630 // FB 47F8000000000000 # primitive(5185894970917126144)
631 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((128LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
632 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large single exp", 5.104235503814077E+38); // Exponent too large for single
633
634 // 66 # text(6)
635 // 646664666465 # "dfdfde"
636 // FA 4B800000 # primitive(1266679808)
637 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest single with prec",16777216); // Single with no precision loss
638
639 // 78 18 # text(24)
640 // 626967676573742073696E676C6520776974682070726563 # "biggest single with prec"
641 // FA 4B800000 # primitive(1266679808)
642 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "first single with prec loss",16777217); // Double becuase of precision loss
643
644 // Just a convenient marker when cutting and pasting encoded CBOR
645 QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
646
647 QCBOREncode_CloseMap(&EC);
648
Laurence Lundblade781fd822018-10-01 09:37:52 -0700649 UsefulBufC EncodedHalfs;
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700650 int nReturn = QCBOREncode_Finish2(&EC, &EncodedHalfs);
651 if(nReturn) {
652 return -1;
653 }
654
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530655 if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedSmallest))) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700656 return -3;
657 }
658
659 return 0;
Laurence Lundblade570fab52018-10-13 18:28:27 +0800660}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700661
662
663
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700664#ifdef NAN_EXPERIMENT
665/*
666 Code for checking what the double to float cast does with
667 NaNs. Not run as part of tests. Keep it around to
668 be able to check various platforms and CPUs.
669 */
670
671#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
672#define DOUBLE_NUM_EXPONENT_BITS (11)
673#define DOUBLE_NUM_SIGN_BITS (1)
674
675#define DOUBLE_SIGNIFICAND_SHIFT (0)
676#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
677#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
678
679#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
680#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
681#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
682#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
683
684
685static int NaNExperiments() {
686 double dqNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT);
687 double dsNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | 0x01);
688 double dqNaNPayload = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT | 0xf00f);
689
690 float f1 = (float)dqNaN;
691 float f2 = (float)dsNaN;
692 float f3 = (float)dqNaNPayload;
693
694
695 uint32_t uqNaN = UsefulBufUtil_CopyFloatToUint32((float)dqNaN);
696 uint32_t usNaN = UsefulBufUtil_CopyFloatToUint32((float)dsNaN);
697 uint32_t uqNaNPayload = UsefulBufUtil_CopyFloatToUint32((float)dqNaNPayload);
698
699 // Result of this on x86 is that every NaN is a qNaN. The intel
700 // CVTSD2SS instruction ignores the NaN payload and even converts
701 // a sNaN to a qNaN.
702
703 return 0;
704}
705#endif
706
707
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700708