blob: 807725198e715a0ef0a5822e5175c530b172306a [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 Lundbladed92a6162018-11-01 11:38:35 +07004 Copyright (c) 2018, Laurence Lundblade.
5 All rights reserved.
Laurence Lundblade68a13352018-09-23 02:19:54 -07006
Laurence Lundbladed92a6162018-11-01 11:38:35 +07007 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are
9 met:
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above
13 copyright notice, this list of conditions and the following
14 disclaimer in the documentation and/or other materials provided
15 with the distribution.
16 * Neither the name of The Linux Foundation nor the names of its
17 contributors, nor the name "Laurence Lundblade" may be used to
18 endorse or promote products derived from this software without
19 specific prior written permission.
Laurence Lundblade68a13352018-09-23 02:19:54 -070020
Laurence Lundbladed92a6162018-11-01 11:38:35 +070021 THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
22 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
24 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
25 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
30 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
31 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Laurence Lundblade68a13352018-09-23 02:19:54 -070032 ==============================================================================*/
Laurence Lundblade68a13352018-09-23 02:19:54 -070033// Created by Laurence Lundblade on 9/19/18.
Laurence Lundblade781fd822018-10-01 09:37:52 -070034
Laurence Lundblade68a13352018-09-23 02:19:54 -070035
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080036#include "float_tests.h"
Laurence Lundblade68a13352018-09-23 02:19:54 -070037#include "qcbor.h"
Laurence Lundbladed711fb22018-09-26 14:35:22 -070038#include "half_to_double_from_rfc7049.h"
39#include <math.h> // For INFINITY and NAN and isnan()
Laurence Lundblade68a13352018-09-23 02:19:54 -070040
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080041
Laurence Lundbladebb474be2018-10-22 11:53:21 +053042/*
43 Output from http://cbor.me
44 [0.0,
45 1.0,
46 1.100000023841858,
47 1.5,
48 65504.0,
49 100000.0,
50 3.4028234663852886e+38,
51 Infinity,
52 5.960464477539063e-8,
53 0.00006103515625,
54 -4.0,
55 -4.099999904632568,
56 NaN,
57 Infinity,
58 -Infinity,
59 0.0,
60 1.0,
61 1.1,
62 1.5,
63 65504.0,
64 100000.0,
65 3.4028234663852886e+38,
66 1.0e+300,
67 5.960464477539063e-8,
68 0.00006103515625,
69 -4.0,
70 -4.1,
71 NaN,
72 Infinity,
73 -Infinity]
74 */
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080075
Laurence Lundbladebb474be2018-10-22 11:53:21 +053076static uint8_t spExpectedEncodedFloat[] = {
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080077 0x98, 0x1e, 0xfa, 0x00, 0x00, 0x00, 0x00, 0xfa,
78 0x3f, 0x80, 0x00, 0x00, 0xfa, 0x3f, 0x8c, 0xcc,
79 0xcd, 0xfa, 0x3f, 0xc0, 0x00, 0x00, 0xfa, 0x47,
80 0x7f, 0xe0, 0x00, 0xfa, 0x47, 0xc3, 0x50, 0x00,
81 0xfa, 0x7f, 0x7f, 0xff, 0xff, 0xfa, 0x7f, 0x80,
82 0x00, 0x00, 0xfa, 0x33, 0x80, 0x00, 0x00, 0xfa,
83 0x38, 0x80, 0x00, 0x00, 0xfa, 0xc0, 0x80, 0x00,
84 0x00, 0xfa, 0xc0, 0x83, 0x33, 0x33, 0xfa, 0x7f,
85 0xc0, 0x00, 0x00, 0xfa, 0x7f, 0x80, 0x00, 0x00,
86 0xfa, 0xff, 0x80, 0x00, 0x00, 0xfb, 0x00, 0x00,
87 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x3f,
88 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb,
89 0x3f, 0xf1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a,
90 0xfb, 0x3f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
91 0x00, 0xfb, 0x40, 0xef, 0xfc, 0x00, 0x00, 0x00,
92 0x00, 0x00, 0xfb, 0x40, 0xf8, 0x6a, 0x00, 0x00,
93 0x00, 0x00, 0x00, 0xfb, 0x47, 0xef, 0xff, 0xff,
94 0xe0, 0x00, 0x00, 0x00, 0xfb, 0x7e, 0x37, 0xe4,
95 0x3c, 0x88, 0x00, 0x75, 0x9c, 0xfb, 0x3e, 0x70,
96 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x3f,
97 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb,
98 0xc0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99 0xfb, 0xc0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66,
100 0x66, 0xfb, 0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00,
101 0x00, 0x00, 0xfb, 0x7f, 0xf0, 0x00, 0x00, 0x00,
102 0x00, 0x00, 0x00, 0xfb, 0xff, 0xf0, 0x00, 0x00,
103 0x00, 0x00, 0x00, 0x00};
104
105
106int FloatValuesTest1()
107{
108 QCBOREncodeContext ECtx;
109 int nReturn = 0;
110
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530111 UsefulBuf_MAKE_STACK_UB(EncodedStorage, 220);
Laurence Lundblade2d85ce42018-10-12 14:12:47 +0800112
113 QCBOREncode_Init(&ECtx, EncodedStorage);
114 QCBOREncode_OpenArray(&ECtx);
115
116 // These are all samples published
117 // in RFC 7049.
118 QCBOREncode_AddFloat(&ECtx, 0.0);
119 QCBOREncode_AddFloat(&ECtx, 1.0);
120 QCBOREncode_AddFloat(&ECtx, 1.1); // appx
121 QCBOREncode_AddFloat(&ECtx, 1.5);
122 QCBOREncode_AddFloat(&ECtx, 65504.0);
123 QCBOREncode_AddFloat(&ECtx, 100000.0);
124 QCBOREncode_AddFloat(&ECtx, 3.4028234663852886e+38);
125 QCBOREncode_AddFloat(&ECtx, 1.0e+300); // Infinity?
126 QCBOREncode_AddFloat(&ECtx, 5.960464477539063e-8);
127 QCBOREncode_AddFloat(&ECtx, 0.00006103515625);
128 QCBOREncode_AddFloat(&ECtx, -4.0);
129 QCBOREncode_AddFloat(&ECtx, -4.1); // appx
130
131 QCBOREncode_AddFloat(&ECtx, NAN);
132 QCBOREncode_AddFloat(&ECtx, INFINITY);
133 QCBOREncode_AddFloat(&ECtx, -INFINITY);
134
135
136 QCBOREncode_AddDouble(&ECtx, 0.0);
137 QCBOREncode_AddDouble(&ECtx, 1.0);
138 QCBOREncode_AddDouble(&ECtx, 1.1); // appx
139 QCBOREncode_AddDouble(&ECtx, 1.5);
140 QCBOREncode_AddDouble(&ECtx, 65504.0);
141 QCBOREncode_AddDouble(&ECtx, 100000.0);
142 QCBOREncode_AddDouble(&ECtx, 3.4028234663852886e+38);
143 QCBOREncode_AddDouble(&ECtx, 1.0e+300); // Infinity?
144 QCBOREncode_AddDouble(&ECtx, 5.960464477539063e-8);
145 QCBOREncode_AddDouble(&ECtx, 0.00006103515625);
146 QCBOREncode_AddDouble(&ECtx, -4.0);
147 QCBOREncode_AddDouble(&ECtx, -4.1); // appx
148
149 QCBOREncode_AddDouble(&ECtx, NAN);
150 QCBOREncode_AddDouble(&ECtx, INFINITY);
151 QCBOREncode_AddDouble(&ECtx, -INFINITY);
152
153 QCBOREncode_CloseArray(&ECtx);
154
155 UsefulBufC Encoded;
156 if(QCBOREncode_Finish2(&ECtx, &Encoded)) {
157 nReturn = -1;
158 }
159
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530160 if(UsefulBuf_Compare(Encoded, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedEncodedFloat))) {
Laurence Lundblade2d85ce42018-10-12 14:12:47 +0800161 nReturn = -2;
162 }
163
164 //printencoded(pEncoded, nEncodedLen);
165
166 return(nReturn);
167}
168
169
170
171
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530172static const uint8_t spExpectedHalf[] = {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700173 0xB1,
Laurence Lundblade68a13352018-09-23 02:19:54 -0700174 0x64,
175 0x7A, 0x65, 0x72, 0x6F,
176 0xF9, 0x00, 0x00, // 0.000
177 0x6A,
178 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
179 0xF9, 0x7C, 0x00, // Infinity
180 0x73,
181 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
182 0xF9, 0xFC, 0x00, // -Inifinity
183 0x63,
184 0x4E, 0x61, 0x4E,
185 0xF9, 0x7E, 0x00, // NaN
186 0x63,
187 0x6F, 0x6E, 0x65,
188 0xF9, 0x3C, 0x00, // 1.0
189 0x69,
190 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
191 0xF9, 0x35, 0x55, // 0.333251953125
192 0x76,
193 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
194 0xF9, 0x7B, 0xFF, // 65504.0
195 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,
196 0xF9, 0x7C, 0x00, // Infinity
197 0x72,
198 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
199 0xF9, 0x00, 0x01, // 0.000000059604
200 0x6F,
201 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
202 0xF9, 0x03, 0xFF, // 0.0000609755516
203 0x71,
204 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
205 0xF9, 0x04, 0x00, // 0.000061988
206 0x70,
207 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65,
208 0xF9, 0x00, 0x00,
209 0x03,
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700210 0xF9, 0xC0, 0x00, // -2
211 0x04,
212 0xF9, 0x7E, 0x00, // qNaN
213 0x05,
214 0xF9, 0x7C, 0x01, // sNaN
215 0x06,
216 0xF9, 0x7E, 0x0F, // qNaN with payload 0x0f
217 0x07,
218 0xF9, 0x7C, 0x0F, // sNaN with payload 0x0f
219
Laurence Lundblade68a13352018-09-23 02:19:54 -0700220};
221
222
223
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530224int HalfPrecisionEncodeBasicTests()
Laurence Lundblade68a13352018-09-23 02:19:54 -0700225{
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530226 UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, 250);
Laurence Lundblade68a13352018-09-23 02:19:54 -0700227
228 QCBOREncodeContext EC;
229 QCBOREncode_Init(&EC, EncodedHalfsMem);
230 // These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
231 QCBOREncode_OpenMap(&EC);
232 QCBOREncode_AddFloatAsHalfToMap(&EC, "zero", 0.00F);
233 QCBOREncode_AddFloatAsHalfToMap(&EC, "infinitity", INFINITY);
234 QCBOREncode_AddFloatAsHalfToMap(&EC, "negative infinitity", -INFINITY);
235 QCBOREncode_AddFloatAsHalfToMap(&EC, "NaN", NAN);
236 QCBOREncode_AddFloatAsHalfToMap(&EC, "one", 1.0F);
237 QCBOREncode_AddFloatAsHalfToMap(&EC, "one third", 0.333251953125F);
238 QCBOREncode_AddFloatAsHalfToMap(&EC, "largest half-precision",65504.0F);
239 // Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which is larger than 15, the largest half-precision exponent
240 QCBOREncode_AddFloatAsHalfToMap(&EC, "too-large half-precision", 65536.0F);
241 // Should convert to smallest possible half precision which is encodded as 0x00 0x01 or 5.960464477539063e-8
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700242 QCBOREncode_AddFloatAsHalfToMap(&EC, "smallest subnormal", 0.0000000596046448F);
Laurence Lundblade68a13352018-09-23 02:19:54 -0700243 QCBOREncode_AddFloatAsHalfToMap(&EC, "smallest normal", 0.0000610351526F); // in hex single is 0x387fffff, exponent -15, significand 7fffff
244 QCBOREncode_AddFloatAsHalfToMap(&EC, "biggest subnormal", 0.0000610351563F); // in hex single is 0x38800000, exponent -14, significand 0
245 QCBOREncode_AddFloatAsHalfToMap(&EC, "subnormal single", 4e-40F);
246 QCBOREncode_AddFloatAsHalfToMapN(&EC, 3, -2.0F);
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700247 QCBOREncode_AddFloatAsHalfToMapN(&EC, 4, UsefulBufUtil_CopyUint32ToFloat(0x7fc00000L)); // qNaN
248 QCBOREncode_AddFloatAsHalfToMapN(&EC, 5, UsefulBufUtil_CopyUint32ToFloat(0x7f800001L)); // sNaN
249 QCBOREncode_AddFloatAsHalfToMapN(&EC, 6, UsefulBufUtil_CopyUint32ToFloat(0x7fc0f00fL)); // qNaN with payload
250 QCBOREncode_AddFloatAsHalfToMapN(&EC, 7, UsefulBufUtil_CopyUint32ToFloat(0x7f80f00fL)); // sNaN with payload
Laurence Lundblade68a13352018-09-23 02:19:54 -0700251 QCBOREncode_CloseMap(&EC);
252
Laurence Lundblade781fd822018-10-01 09:37:52 -0700253 UsefulBufC EncodedHalfs;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700254 int nReturn = QCBOREncode_Finish2(&EC, &EncodedHalfs);
Laurence Lundblade68a13352018-09-23 02:19:54 -0700255 if(nReturn) {
256 return -1;
257 }
258
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530259 if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedHalf))) {
Laurence Lundblade68a13352018-09-23 02:19:54 -0700260 return -3;
261 }
262
263 return 0;
264}
265
266
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530267int HalfPrecisionDecodeBasicTests()
Laurence Lundblade68a13352018-09-23 02:19:54 -0700268{
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530269 UsefulBufC HalfPrecision = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedHalf);
Laurence Lundblade68a13352018-09-23 02:19:54 -0700270
271 QCBORDecodeContext DC;
272 QCBORDecode_Init(&DC, HalfPrecision, 0);
273
274 QCBORItem Item;
275
276 QCBORDecode_GetNext(&DC, &Item);
277 if(Item.uDataType != QCBOR_TYPE_MAP) {
278 return -1;
279 }
280
281 QCBORDecode_GetNext(&DC, &Item);
282 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700283 return -2;
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 -3;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700289 }
290
291 QCBORDecode_GetNext(&DC, &Item);
292 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != -INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700293 return -4;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700294 }
295
296 QCBORDecode_GetNext(&DC, &Item); // TODO, is this really converting right? It is carrying payload, but this confuses things.
297 if(Item.uDataType != QCBOR_TYPE_FLOAT || !isnan(Item.val.fnum)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700298 return -5;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700299 }
300
301 QCBORDecode_GetNext(&DC, &Item);
302 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 1.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700303 return -6;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700304 }
305
306 QCBORDecode_GetNext(&DC, &Item);
307 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.333251953125F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700308 return -7;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700309 }
310
311 QCBORDecode_GetNext(&DC, &Item);
312 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 65504.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700313 return -8;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700314 }
315
316 QCBORDecode_GetNext(&DC, &Item);
317 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700318 return -9;
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.0000000596046448F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700323 return -10;
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.0000609755516F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700328 return -11;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700329 }
330
331 QCBORDecode_GetNext(&DC, &Item); // TODO check this
332 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0000610351563F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700333 return -12;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700334 }
335
336 QCBORDecode_GetNext(&DC, &Item);
337 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700338 return -13;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700339 }
340
341 QCBORDecode_GetNext(&DC, &Item);
342 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != -2.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700343 return -14;
344 }
345
346 QCBORDecode_GetNext(&DC, &Item);
347 if(Item.uDataType != QCBOR_TYPE_FLOAT || UsefulBufUtil_CopyFloatToUint32(Item.val.fnum) != 0x7fc00000L) {
348 return -15;
349 }
350 QCBORDecode_GetNext(&DC, &Item);
351 if(Item.uDataType != QCBOR_TYPE_FLOAT || UsefulBufUtil_CopyFloatToUint32(Item.val.fnum) != 0x7f800001) {
352 return -16;
353 }
354 QCBORDecode_GetNext(&DC, &Item);
355 if(Item.uDataType != QCBOR_TYPE_FLOAT || UsefulBufUtil_CopyFloatToUint32(Item.val.fnum) != 0x7fc0000f) {
356 return -17;
357 }
358 QCBORDecode_GetNext(&DC, &Item);
359 if(Item.uDataType != QCBOR_TYPE_FLOAT || UsefulBufUtil_CopyFloatToUint32(Item.val.fnum) != 0x7f80000f) {
360 return -18;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700361 }
362
363 if(QCBORDecode_Finish(&DC)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700364 return -19;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700365 }
366
367 return 0;
368}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700369
370
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530371int HalfPrecisionTransitiveTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700372{
373 for(uint32_t uHalfP = 0; uHalfP < 0xffff; uHalfP += 1) {
374 // Contruct the CBOR for the half-precision float by hand
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530375 UsefulBuf_MAKE_STACK_UB(EncodedCBORMem, 3);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700376 UsefulOutBuf UOB;
377 UsefulOutBuf_Init(&UOB, EncodedCBORMem);
378
379 const uint8_t uHalfPrecInitialByte = HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5); // 0xf9
380 UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
381 UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
382
383
384 // Now parse the hand-constructed CBOR. This will invoke the conversion to a float
385 QCBORDecodeContext DC;
386 QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
387
388 QCBORItem Item;
389 QCBORDecode_GetNext(&DC, &Item);
390 if(Item.uDataType != QCBOR_TYPE_FLOAT) {
391 return -1;
392 }
393
394 //printf("%04x QCBOR:%15.15f \n", uHalfP,Item.val.fnum);
395
396
397 // Now generate CBOR with the half-precision value. This will invoke the conversion from float to half
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530398 UsefulBuf_MAKE_STACK_UB(OtherEncodedCBORMem, 5);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700399 QCBOREncodeContext EC;
400 QCBOREncode_Init(&EC, OtherEncodedCBORMem);
401 QCBOREncode_AddFloatAsHalf(&EC, Item.val.fnum);
Laurence Lundblade781fd822018-10-01 09:37:52 -0700402 UsefulBufC EnCBOR;
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700403 QCBOREncode_Finish2(&EC, &EnCBOR); // todo check return code
404
405
406 // Finally parse the CBOR by hand to get at half-precision that was actually encoded.
407 UsefulInputBuf UIB;
Laurence Lundblade781fd822018-10-01 09:37:52 -0700408 UsefulInputBuf_Init(&UIB, EnCBOR);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700409 if(UsefulInputBuf_GetByte(&UIB) != uHalfPrecInitialByte) {
410 return -2;
411 }
412 if(UsefulInputBuf_GetUint16(&UIB) != uHalfP) { // the moment of truth did we get back what we started with?
413 return -3;
414 }
415 }
416
417 return 0;
418}
419
420
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530421int HalfPrecisionAgainstRFCCodeTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700422{
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700423 for(uint32_t uHalfP = 0; uHalfP < 0xffff; uHalfP += 60) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700424 unsigned char x[2];
425 x[1] = uHalfP & 0xff;
426 x[0] = uHalfP >> 8;
427 double d = decode_half(x);
428
429 // Contruct the CBOR for the half-precision float by hand
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530430 UsefulBuf_MAKE_STACK_UB(__xx, 3);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700431 UsefulOutBuf UOB;
432 UsefulOutBuf_Init(&UOB, __xx);
433
434 const uint8_t uHalfPrecInitialByte = HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5); // 0xf9
435 UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
436 UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
437
438 // Now parse the hand-constructed CBOR. This will invoke the conversion to a float
439 QCBORDecodeContext DC;
440 QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
441
442 QCBORItem Item;
443
444 QCBORDecode_GetNext(&DC, &Item);
445 if(Item.uDataType != QCBOR_TYPE_FLOAT) {
446 return -1;
447 }
448
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700449 //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 -0700450
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700451 if(isnan(d)) {
452 // The RFC code uses the native instructions which may or may not
453 // handle sNaN, qNaN and NaN payloads correctly. This test just
454 // makes sure it is a NaN and doesn't worry about the type of NaN
455 if(!isnan(Item.val.fnum)) {
456 return -3;
457 }
458 } else {
459 if(Item.val.fnum != d) {
460 return -2;
461 }
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700462 }
463 }
464 return 0;
465}
466
467
468/*
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530469 {"zero": 0.0,
470 "negative zero": -0.0,
471 "infinitity": Infinity,
472 "negative infinitity": -Infinity,
473 "NaN": NaN,
474 "one": 1.0,
475 "one third": 0.333251953125,
476 "largest half-precision": 65504.0,
477 "largest half-precision point one": 65504.1,
478 "too-large half-precision": 65536.0,
479 "smallest subnormal": 5.96046448e-8,
480 "smallest normal": 0.00006103515261202119,
481 "biggest subnormal": 0.00006103515625,
482 "subnormal single": 4.00000646641519e-40,
483 3: -2.0,
484 "large single exp": 2.5521177519070385e+38,
485 "too-large single exp": 5.104235503814077e+38,
486 "biggest single with prec": 16777216.0,
487 "first single with prec loss": 16777217.0,
488 1: "fin"}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700489 */
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530490static const uint8_t spExpectedSmallest[] = {
491 0xB4, 0x64, 0x7A, 0x65, 0x72, 0x6F, 0xF9, 0x00, 0x00, 0x6D,
492 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x7A,
493 0x65, 0x72, 0x6F, 0xF9, 0x80, 0x00, 0x6A, 0x69, 0x6E, 0x66,
494 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79, 0xF9, 0x7C, 0x00,
495 0x73, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20,
496 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
497 0xF9, 0xFC, 0x00, 0x63, 0x4E, 0x61, 0x4E, 0xF9, 0x7E, 0x00,
498 0x63, 0x6F, 0x6E, 0x65, 0xF9, 0x3C, 0x00, 0x69, 0x6F, 0x6E,
499 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0xF9, 0x35, 0x55,
500 0x76, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68,
501 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73,
502 0x69, 0x6F, 0x6E, 0xF9, 0x7B, 0xFF, 0x78, 0x20, 0x6C, 0x61,
503 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66,
504 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
505 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, 0x6F, 0x6E, 0x65,
506 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33, 0x78,
507 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65,
508 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63,
509 0x69, 0x73, 0x69, 0x6F, 0x6E, 0xFA, 0x47, 0x80, 0x00, 0x00,
510 0x72, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
511 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xFB,
512 0x3E, 0x70, 0x00, 0x00, 0x00, 0x1C, 0x5F, 0x68, 0x6F, 0x73,
513 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
514 0x72, 0x6D, 0x61, 0x6C, 0xFA, 0x38, 0x7F, 0xFF, 0xFF, 0x71,
515 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
516 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xF9, 0x04, 0x00,
517 0x70, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
518 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0xFB, 0x37, 0xC1,
519 0x6C, 0x28, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF9, 0xC0, 0x00,
520 0x70, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E,
521 0x67, 0x6C, 0x65, 0x20, 0x65, 0x78, 0x70, 0xFA, 0x7F, 0x40,
522 0x00, 0x00, 0x74, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72,
523 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20,
524 0x65, 0x78, 0x70, 0xFB, 0x47, 0xF8, 0x00, 0x00, 0x00, 0x00,
525 0x00, 0x00, 0x78, 0x18, 0x62, 0x69, 0x67, 0x67, 0x65, 0x73,
526 0x74, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77,
527 0x69, 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0xFA, 0x4B,
528 0x80, 0x00, 0x00, 0x78, 0x1B, 0x66, 0x69, 0x72, 0x73, 0x74,
529 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77, 0x69,
530 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x20, 0x6C, 0x6F,
531 0x73, 0x73, 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00,
532 0x00, 0x01, 0x63, 0x66, 0x69, 0x6E
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700533};
534
535
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530536int DoubleAsSmallestTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700537{
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530538 UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, 420);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700539
540 QCBOREncodeContext EC;
541 QCBOREncode_Init(&EC, EncodedHalfsMem);
542 // These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
543 QCBOREncode_OpenMap(&EC);
544 // 64 # text(4)
545 // 7A65726F # "zero"
546 // F9 0000 # primitive(0)
547 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "zero", 0.00);
548
549 // 64 # text(4)
550 // 7A65726F # "negative zero"
551 // F9 8000 # primitive(0)
552 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative zero", -0.00);
553
554 // 6A # text(10)
555 // 696E66696E6974697479 # "infinitity"
556 // F9 7C00 # primitive(31744)
557 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "infinitity", INFINITY);
558
559 // 73 # text(19)
560 // 6E6567617469766520696E66696E6974697479 # "negative infinitity"
561 // F9 FC00 # primitive(64512)
562 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative infinitity", -INFINITY);
563
564 // 63 # text(3)
565 // 4E614E # "NaN"
566 // F9 7E00 # primitive(32256)
567 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "NaN", NAN);
568
569 // TODO: test a few NaN variants
570
571 // 63 # text(3)
572 // 6F6E65 # "one"
573 // F9 3C00 # primitive(15360)
574 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one", 1.0);
575
576 // 69 # text(9)
577 // 6F6E65207468697264 # "one third"
578 // F9 3555 # primitive(13653)
579 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one third", 0.333251953125);
580
581 // 76 # text(22)
582 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
583 // F9 7BFF # primitive(31743)
584 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision",65504.0);
585
586 // 76 # text(22)
587 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
588 // F9 7BFF # primitive(31743)
589 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision point one",65504.1);
590
591 // Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which is larger than 15, the largest half-precision exponent
592 // 78 18 # text(24)
593 // 746F6F2D6C617267652068616C662D707265636973696F6E # "too-large half-precision"
594 // FA 47800000 # primitive(31743)
595 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large half-precision", 65536.0);
596
597 // The smallest possible half-precision subnormal, but digitis are lost converting
598 // to half, so this turns into a double
599 // 72 # text(18)
600 // 736D616C6C657374207375626E6F726D616C # "smallest subnormal"
601 // FB 3E700000001C5F68 # primitive(4499096027744984936)
602 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest subnormal", 0.0000000596046448);
603
604 // The smallest possible half-precision snormal, but digitis are lost converting
605 // to half, so this turns into a single TODO: confirm this is right
606 // 6F # text(15)
607 // 736D616C6C657374206E6F726D616C # "smallest normal"
608 // FA 387FFFFF # primitive(947912703)
609 // in hex single is 0x387fffff, exponent -15, significand 7fffff
610 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest normal", 0.0000610351526F);
611
612 // 71 # text(17)
613 // 62696767657374207375626E6F726D616C # "biggest subnormal"
614 // F9 0400 # primitive(1024)
615 // in hex single is 0x38800000, exponent -14, significand 0
616 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest subnormal", 0.0000610351563F);
617
618 // 70 # text(16)
619 // 7375626E6F726D616C2073696E676C65 # "subnormal single"
620 // FB 37C16C2800000000 # primitive(4017611261645684736)
621 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "subnormal single", 4e-40F);
622
623 // 03 # unsigned(3)
624 // F9 C000 # primitive(49152)
625 QCBOREncode_AddDoubleAsSmallestToMapN(&EC, 3, -2.0);
626
627 // 70 # text(16)
628 // 6C617267652073696E676C6520657870 # "large single exp"
629 // FA 7F400000 # primitive(2134900736)
630 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((127LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
631 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "large single exp", 2.5521177519070385E+38); // Exponent fits single
632
633 // 74 # text(20)
634 // 746F6F2D6C617267652073696E676C6520657870 # "too-large single exp"
635 // FB 47F8000000000000 # primitive(5185894970917126144)
636 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((128LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
637 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large single exp", 5.104235503814077E+38); // Exponent too large for single
638
639 // 66 # text(6)
640 // 646664666465 # "dfdfde"
641 // FA 4B800000 # primitive(1266679808)
642 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest single with prec",16777216); // Single with no precision loss
643
644 // 78 18 # text(24)
645 // 626967676573742073696E676C6520776974682070726563 # "biggest single with prec"
646 // FA 4B800000 # primitive(1266679808)
647 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "first single with prec loss",16777217); // Double becuase of precision loss
648
649 // Just a convenient marker when cutting and pasting encoded CBOR
650 QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
651
652 QCBOREncode_CloseMap(&EC);
653
Laurence Lundblade781fd822018-10-01 09:37:52 -0700654 UsefulBufC EncodedHalfs;
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700655 int nReturn = QCBOREncode_Finish2(&EC, &EncodedHalfs);
656 if(nReturn) {
657 return -1;
658 }
659
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530660 if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedSmallest))) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700661 return -3;
662 }
663
664 return 0;
Laurence Lundblade570fab52018-10-13 18:28:27 +0800665}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700666
667
668
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700669#ifdef NAN_EXPERIMENT
670/*
671 Code for checking what the double to float cast does with
672 NaNs. Not run as part of tests. Keep it around to
673 be able to check various platforms and CPUs.
674 */
675
676#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
677#define DOUBLE_NUM_EXPONENT_BITS (11)
678#define DOUBLE_NUM_SIGN_BITS (1)
679
680#define DOUBLE_SIGNIFICAND_SHIFT (0)
681#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
682#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
683
684#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
685#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
686#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
687#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
688
689
690static int NaNExperiments() {
691 double dqNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT);
692 double dsNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | 0x01);
693 double dqNaNPayload = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT | 0xf00f);
694
695 float f1 = (float)dqNaN;
696 float f2 = (float)dsNaN;
697 float f3 = (float)dqNaNPayload;
698
699
700 uint32_t uqNaN = UsefulBufUtil_CopyFloatToUint32((float)dqNaN);
701 uint32_t usNaN = UsefulBufUtil_CopyFloatToUint32((float)dsNaN);
702 uint32_t uqNaNPayload = UsefulBufUtil_CopyFloatToUint32((float)dqNaNPayload);
703
704 // Result of this on x86 is that every NaN is a qNaN. The intel
705 // CVTSD2SS instruction ignores the NaN payload and even converts
706 // a sNaN to a qNaN.
707
708 return 0;
709}
710#endif
711
712
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700713