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