blob: d43039d25a0cf73fbd2be3100c0720ce3ac7a087 [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 Lundbladeee851742020-01-08 08:37:05 -08004 Copyright (c) 2018-2020, Laurence Lundblade. All rights reserved.
Laurence Lundblade035bd782019-01-21 17:01:31 -08005
Laurence Lundbladea3fd49f2019-01-21 10:16:22 -08006 SPDX-License-Identifier: BSD-3-Clause
Laurence Lundblade035bd782019-01-21 17:01:31 -08007
Laurence Lundbladea3fd49f2019-01-21 10:16:22 -08008 See BSD-3-Clause license in README.md
Laurence Lundblade035bd782019-01-21 17:01:31 -08009
Laurence Lundbladea3fd49f2019-01-21 10:16:22 -080010 Created on 9/19/18
Laurence Lundbladeee851742020-01-08 08:37:05 -080011 =============================================================================*/
Laurence Lundblade68a13352018-09-23 02:19:54 -070012
Laurence Lundbladeb275cdc2020-07-12 12:34:38 -070013#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
Laurence Lundblade9682a532020-06-06 18:33:04 -070014
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080015#include "float_tests.h"
Laurence Lundblade844bb5c2020-03-01 17:27:25 -080016#include "qcbor/qcbor_encode.h"
17#include "qcbor/qcbor_decode.h"
Laurence Lundbladed711fb22018-09-26 14:35:22 -070018#include "half_to_double_from_rfc7049.h"
19#include <math.h> // For INFINITY and NAN and isnan()
Laurence Lundblade68a13352018-09-23 02:19:54 -070020
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080021
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080022
Laurence Lundbladebb474be2018-10-22 11:53:21 +053023static const uint8_t spExpectedHalf[] = {
Laurence Lundblade7d40d812018-09-30 02:44:01 -070024 0xB1,
Laurence Lundblade68a13352018-09-23 02:19:54 -070025 0x64,
26 0x7A, 0x65, 0x72, 0x6F,
27 0xF9, 0x00, 0x00, // 0.000
28 0x6A,
29 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
30 0xF9, 0x7C, 0x00, // Infinity
31 0x73,
Laurence Lundbladeee851742020-01-08 08:37:05 -080032 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6E,
33 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
Laurence Lundblade68a13352018-09-23 02:19:54 -070034 0xF9, 0xFC, 0x00, // -Inifinity
35 0x63,
36 0x4E, 0x61, 0x4E,
37 0xF9, 0x7E, 0x00, // NaN
38 0x63,
39 0x6F, 0x6E, 0x65,
40 0xF9, 0x3C, 0x00, // 1.0
41 0x69,
42 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
43 0xF9, 0x35, 0x55, // 0.333251953125
44 0x76,
Laurence Lundbladeee851742020-01-08 08:37:05 -080045 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C,
46 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
Laurence Lundblade68a13352018-09-23 02:19:54 -070047 0xF9, 0x7B, 0xFF, // 65504.0
Laurence Lundbladeee851742020-01-08 08:37:05 -080048 0x78, 0x18,
49 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x68,
50 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69,
51 0x6F, 0x6E,
Laurence Lundblade68a13352018-09-23 02:19:54 -070052 0xF9, 0x7C, 0x00, // Infinity
53 0x72,
Laurence Lundbladeee851742020-01-08 08:37:05 -080054 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
55 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
Laurence Lundblade68a13352018-09-23 02:19:54 -070056 0xF9, 0x00, 0x01, // 0.000000059604
57 0x6F,
Laurence Lundbladeee851742020-01-08 08:37:05 -080058 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
59 0x72, 0x6D, 0x61, 0x6C,
Laurence Lundblade68a13352018-09-23 02:19:54 -070060 0xF9, 0x03, 0xFF, // 0.0000609755516
61 0x71,
Laurence Lundbladeee851742020-01-08 08:37:05 -080062 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62,
63 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
Laurence Lundblade68a13352018-09-23 02:19:54 -070064 0xF9, 0x04, 0x00, // 0.000061988
65 0x70,
Laurence Lundbladeee851742020-01-08 08:37:05 -080066 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x73,
67 0x69, 0x6E, 0x67, 0x6C, 0x65,
Laurence Lundblade68a13352018-09-23 02:19:54 -070068 0xF9, 0x00, 0x00,
69 0x03,
Laurence Lundblade7d40d812018-09-30 02:44:01 -070070 0xF9, 0xC0, 0x00, // -2
71 0x04,
72 0xF9, 0x7E, 0x00, // qNaN
73 0x05,
74 0xF9, 0x7C, 0x01, // sNaN
75 0x06,
76 0xF9, 0x7E, 0x0F, // qNaN with payload 0x0f
77 0x07,
78 0xF9, 0x7C, 0x0F, // sNaN with payload 0x0f
Laurence Lundblade3aee3a32018-12-17 16:17:45 -080079
Laurence Lundblade68a13352018-09-23 02:19:54 -070080};
81
82
Laurence Lundbladec5fef682020-01-25 11:38:45 -080083int32_t HalfPrecisionDecodeBasicTests()
Laurence Lundblade68a13352018-09-23 02:19:54 -070084{
Laurence Lundbladebb474be2018-10-22 11:53:21 +053085 UsefulBufC HalfPrecision = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedHalf);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -080086
Laurence Lundblade68a13352018-09-23 02:19:54 -070087 QCBORDecodeContext DC;
88 QCBORDecode_Init(&DC, HalfPrecision, 0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -080089
Laurence Lundblade68a13352018-09-23 02:19:54 -070090 QCBORItem Item;
91
92 QCBORDecode_GetNext(&DC, &Item);
93 if(Item.uDataType != QCBOR_TYPE_MAP) {
94 return -1;
95 }
96
97 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +070098 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -070099 return -2;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700100 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800101
Laurence Lundblade68a13352018-09-23 02:19:54 -0700102 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700103 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700104 return -3;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700105 }
106
107 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700108 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700109 return -4;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700110 }
111
Laurence Lundbladeee851742020-01-08 08:37:05 -0800112 // TODO, is this really converting right? It is carrying payload, but
113 // this confuses things.
114 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700115 if(Item.uDataType != QCBOR_TYPE_DOUBLE || !isnan(Item.val.dfnum)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700116 return -5;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700117 }
118
119 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700120 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 1.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700121 return -6;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700122 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800123
Laurence Lundblade68a13352018-09-23 02:19:54 -0700124 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700125 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.333251953125F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700126 return -7;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700127 }
128
129 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700130 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 65504.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700131 return -8;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700132 }
133
134 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700135 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700136 return -9;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700137 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800138
Laurence Lundblade68a13352018-09-23 02:19:54 -0700139 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700140 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000000596046448F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700141 return -10;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700142 }
143
144 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700145 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000609755516F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700146 return -11;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700147 }
148
149 QCBORDecode_GetNext(&DC, &Item); // TODO check this
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700150 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000610351563F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700151 return -12;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700152 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800153
154 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700155 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700156 return -13;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700157 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800158
Laurence Lundblade68a13352018-09-23 02:19:54 -0700159 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700160 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -2.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700161 return -14;
162 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800163
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700164 // TODO: double check these four tests
165 QCBORDecode_GetNext(&DC, &Item); // qNaN
Laurence Lundbladeee851742020-01-08 08:37:05 -0800166 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
167 UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff8000000000000ULL) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700168 return -15;
169 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700170 QCBORDecode_GetNext(&DC, &Item); // sNaN
Laurence Lundbladeee851742020-01-08 08:37:05 -0800171 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
172 UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff0000000000001ULL) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700173 return -16;
174 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700175 QCBORDecode_GetNext(&DC, &Item); // qNaN with payload 0x0f
Laurence Lundbladeee851742020-01-08 08:37:05 -0800176 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
177 UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff800000000000fULL) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700178 return -17;
179 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700180 QCBORDecode_GetNext(&DC, &Item); // sNaN with payload 0x0f
Laurence Lundbladeee851742020-01-08 08:37:05 -0800181 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
182 UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff000000000000fULL) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700183 return -18;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700184 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700185
Laurence Lundblade68a13352018-09-23 02:19:54 -0700186 if(QCBORDecode_Finish(&DC)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700187 return -19;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700188 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800189
Laurence Lundblade68a13352018-09-23 02:19:54 -0700190 return 0;
191}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700192
193
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700194
195
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800196int32_t HalfPrecisionAgainstRFCCodeTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700197{
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700198 for(uint32_t uHalfP = 0; uHalfP < 0xffff; uHalfP += 60) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700199 unsigned char x[2];
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800200 x[1] = (uint8_t)(uHalfP & 0xff);
201 x[0] = (uint8_t)(uHalfP >> 8); // uHalfP is always less than 0xffff
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700202 double d = decode_half(x);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800203
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700204 // Contruct the CBOR for the half-precision float by hand
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530205 UsefulBuf_MAKE_STACK_UB(__xx, 3);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700206 UsefulOutBuf UOB;
207 UsefulOutBuf_Init(&UOB, __xx);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800208
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800209 const uint8_t uHalfPrecInitialByte = (uint8_t)(HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5)); // 0xf9
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700210 UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
211 UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
212
Laurence Lundbladeee851742020-01-08 08:37:05 -0800213 // Now parse the hand-constructed CBOR. This will invoke the
214 // conversion to a float
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700215 QCBORDecodeContext DC;
216 QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800217
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700218 QCBORItem Item;
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800219
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700220 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700221 if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700222 return -1;
223 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800224
Laurence Lundbladeee851742020-01-08 08:37:05 -0800225 //printf("%04x QCBOR:%15.15f RFC: %15.15f (%8x)\n",
226 // uHalfP, Item.val.fnum, d , UsefulBufUtil_CopyFloatToUint32(d));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800227
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700228 if(isnan(d)) {
229 // The RFC code uses the native instructions which may or may not
230 // handle sNaN, qNaN and NaN payloads correctly. This test just
231 // makes sure it is a NaN and doesn't worry about the type of NaN
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700232 if(!isnan(Item.val.dfnum)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700233 return -3;
234 }
235 } else {
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700236 if(Item.val.dfnum != d) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700237 return -2;
238 }
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700239 }
240 }
241 return 0;
242}
243
244
245/*
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530246 {"zero": 0.0,
247 "negative zero": -0.0,
248 "infinitity": Infinity,
249 "negative infinitity": -Infinity,
250 "NaN": NaN,
251 "one": 1.0,
252 "one third": 0.333251953125,
253 "largest half-precision": 65504.0,
254 "largest half-precision point one": 65504.1,
255 "too-large half-precision": 65536.0,
256 "smallest subnormal": 5.96046448e-8,
257 "smallest normal": 0.00006103515261202119,
258 "biggest subnormal": 0.00006103515625,
259 "subnormal single": 4.00000646641519e-40,
260 3: -2.0,
261 "large single exp": 2.5521177519070385e+38,
262 "too-large single exp": 5.104235503814077e+38,
263 "biggest single with prec": 16777216.0,
264 "first single with prec loss": 16777217.0,
265 1: "fin"}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700266 */
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530267static const uint8_t spExpectedSmallest[] = {
268 0xB4, 0x64, 0x7A, 0x65, 0x72, 0x6F, 0xF9, 0x00, 0x00, 0x6D,
269 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x7A,
270 0x65, 0x72, 0x6F, 0xF9, 0x80, 0x00, 0x6A, 0x69, 0x6E, 0x66,
271 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79, 0xF9, 0x7C, 0x00,
272 0x73, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20,
273 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
274 0xF9, 0xFC, 0x00, 0x63, 0x4E, 0x61, 0x4E, 0xF9, 0x7E, 0x00,
275 0x63, 0x6F, 0x6E, 0x65, 0xF9, 0x3C, 0x00, 0x69, 0x6F, 0x6E,
276 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0xF9, 0x35, 0x55,
277 0x76, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68,
278 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73,
279 0x69, 0x6F, 0x6E, 0xF9, 0x7B, 0xFF, 0x78, 0x20, 0x6C, 0x61,
280 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66,
281 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
282 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, 0x6F, 0x6E, 0x65,
283 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33, 0x78,
284 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65,
285 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63,
286 0x69, 0x73, 0x69, 0x6F, 0x6E, 0xFA, 0x47, 0x80, 0x00, 0x00,
287 0x72, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
288 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xFB,
289 0x3E, 0x70, 0x00, 0x00, 0x00, 0x1C, 0x5F, 0x68, 0x6F, 0x73,
290 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
291 0x72, 0x6D, 0x61, 0x6C, 0xFA, 0x38, 0x7F, 0xFF, 0xFF, 0x71,
292 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
293 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xF9, 0x04, 0x00,
294 0x70, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
295 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0xFB, 0x37, 0xC1,
296 0x6C, 0x28, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF9, 0xC0, 0x00,
297 0x70, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E,
298 0x67, 0x6C, 0x65, 0x20, 0x65, 0x78, 0x70, 0xFA, 0x7F, 0x40,
299 0x00, 0x00, 0x74, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72,
300 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20,
301 0x65, 0x78, 0x70, 0xFB, 0x47, 0xF8, 0x00, 0x00, 0x00, 0x00,
302 0x00, 0x00, 0x78, 0x18, 0x62, 0x69, 0x67, 0x67, 0x65, 0x73,
303 0x74, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77,
304 0x69, 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0xFA, 0x4B,
305 0x80, 0x00, 0x00, 0x78, 0x1B, 0x66, 0x69, 0x72, 0x73, 0x74,
306 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77, 0x69,
307 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x20, 0x6C, 0x6F,
308 0x73, 0x73, 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00,
309 0x00, 0x01, 0x63, 0x66, 0x69, 0x6E
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700310};
311
312
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800313int32_t DoubleAsSmallestTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700314{
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530315 UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, 420);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800316
Laurence Lundblade067035b2018-11-28 17:35:25 -0800317#define QCBOREncode_AddDoubleAsSmallestToMap QCBOREncode_AddDoubleToMap
318#define QCBOREncode_AddDoubleAsSmallestToMapN QCBOREncode_AddDoubleToMapN
319
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800320
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700321 QCBOREncodeContext EC;
322 QCBOREncode_Init(&EC, EncodedHalfsMem);
323 // These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
324 QCBOREncode_OpenMap(&EC);
325 // 64 # text(4)
326 // 7A65726F # "zero"
327 // F9 0000 # primitive(0)
328 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "zero", 0.00);
329
330 // 64 # text(4)
331 // 7A65726F # "negative zero"
332 // F9 8000 # primitive(0)
333 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative zero", -0.00);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800334
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700335 // 6A # text(10)
336 // 696E66696E6974697479 # "infinitity"
337 // F9 7C00 # primitive(31744)
338 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "infinitity", INFINITY);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800339
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700340 // 73 # text(19)
341 // 6E6567617469766520696E66696E6974697479 # "negative infinitity"
342 // F9 FC00 # primitive(64512)
343 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative infinitity", -INFINITY);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800344
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700345 // 63 # text(3)
346 // 4E614E # "NaN"
347 // F9 7E00 # primitive(32256)
348 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "NaN", NAN);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800349
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700350 // TODO: test a few NaN variants
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800351
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700352 // 63 # text(3)
353 // 6F6E65 # "one"
354 // F9 3C00 # primitive(15360)
355 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one", 1.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800356
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700357 // 69 # text(9)
358 // 6F6E65207468697264 # "one third"
359 // F9 3555 # primitive(13653)
360 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one third", 0.333251953125);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800361
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700362 // 76 # text(22)
Laurence Lundbladeee851742020-01-08 08:37:05 -0800363 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700364 // F9 7BFF # primitive(31743)
365 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision",65504.0);
366
367 // 76 # text(22)
Laurence Lundbladeee851742020-01-08 08:37:05 -0800368 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700369 // F9 7BFF # primitive(31743)
370 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision point one",65504.1);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800371
Laurence Lundbladeee851742020-01-08 08:37:05 -0800372 // Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which
373 // is larger than 15, the largest half-precision exponent
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700374 // 78 18 # text(24)
375 // 746F6F2D6C617267652068616C662D707265636973696F6E # "too-large half-precision"
376 // FA 47800000 # primitive(31743)
377 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large half-precision", 65536.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800378
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700379 // The smallest possible half-precision subnormal, but digitis are lost converting
380 // to half, so this turns into a double
381 // 72 # text(18)
382 // 736D616C6C657374207375626E6F726D616C # "smallest subnormal"
383 // FB 3E700000001C5F68 # primitive(4499096027744984936)
384 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest subnormal", 0.0000000596046448);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800385
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700386 // The smallest possible half-precision snormal, but digitis are lost converting
387 // to half, so this turns into a single TODO: confirm this is right
388 // 6F # text(15)
389 // 736D616C6C657374206E6F726D616C # "smallest normal"
390 // FA 387FFFFF # primitive(947912703)
391 // in hex single is 0x387fffff, exponent -15, significand 7fffff
392 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest normal", 0.0000610351526F);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800393
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700394 // 71 # text(17)
395 // 62696767657374207375626E6F726D616C # "biggest subnormal"
396 // F9 0400 # primitive(1024)
397 // in hex single is 0x38800000, exponent -14, significand 0
398 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest subnormal", 0.0000610351563F);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800399
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700400 // 70 # text(16)
401 // 7375626E6F726D616C2073696E676C65 # "subnormal single"
402 // FB 37C16C2800000000 # primitive(4017611261645684736)
403 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "subnormal single", 4e-40F);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800404
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700405 // 03 # unsigned(3)
406 // F9 C000 # primitive(49152)
407 QCBOREncode_AddDoubleAsSmallestToMapN(&EC, 3, -2.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800408
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700409 // 70 # text(16)
410 // 6C617267652073696E676C6520657870 # "large single exp"
411 // FA 7F400000 # primitive(2134900736)
412 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((127LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
413 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "large single exp", 2.5521177519070385E+38); // Exponent fits single
414
415 // 74 # text(20)
416 // 746F6F2D6C617267652073696E676C6520657870 # "too-large single exp"
417 // FB 47F8000000000000 # primitive(5185894970917126144)
418 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((128LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
Laurence Lundbladeee851742020-01-08 08:37:05 -0800419 // Exponent too large for single
420 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large single exp", 5.104235503814077E+38);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700421
422 // 66 # text(6)
423 // 646664666465 # "dfdfde"
424 // FA 4B800000 # primitive(1266679808)
Laurence Lundbladeee851742020-01-08 08:37:05 -0800425 // Single with no precision loss
426 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest single with prec", 16777216);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800427
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700428 // 78 18 # text(24)
429 // 626967676573742073696E676C6520776974682070726563 # "biggest single with prec"
430 // FA 4B800000 # primitive(1266679808)
Laurence Lundbladeee851742020-01-08 08:37:05 -0800431 // Double becuase of precision loss
432 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "first single with prec loss", 16777217);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800433
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700434 // Just a convenient marker when cutting and pasting encoded CBOR
435 QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
436
437 QCBOREncode_CloseMap(&EC);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800438
Laurence Lundblade781fd822018-10-01 09:37:52 -0700439 UsefulBufC EncodedHalfs;
Laurence Lundblade29497c02020-07-11 15:44:03 -0700440 QCBORError uErr = QCBOREncode_Finish(&EC, &EncodedHalfs);
441 if(uErr) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700442 return -1;
443 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800444
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530445 if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedSmallest))) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700446 return -3;
447 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800448
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700449 return 0;
Laurence Lundblade570fab52018-10-13 18:28:27 +0800450}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700451
452
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700453/*
454[0.0, 3.14, 0.0, NaN, Infinity, 0.0, 3.140000104904175, 0.0, NaN, Infinity,
455 {100: 0.0, 101: 3.1415926, "euler": 2.718281828459045, 105: 0.0,
456 102: 0.0, 103: 3.141592502593994, "euler2": 2.7182817459106445, 106: 0.0}]
457 */
458static const uint8_t spExpectedFloats[] = {
459 0x8B,
460 0xF9, 0x00, 0x00,
461 0xFB, 0x40, 0x09, 0x1E, 0xB8, 0x51, 0xEB, 0x85, 0x1F,
462 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
463 0xFB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
464 0xFB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
465 0xF9, 0x00, 0x00,
466 0xFA, 0x40, 0x48, 0xF5, 0xC3,
467 0xFA, 0x00, 0x00, 0x00, 0x00,
468 0xFA, 0x7F, 0xC0, 0x00, 0x00,
469 0xFA, 0x7F, 0x80, 0x00, 0x00,
470 0xA8,
471 0x18, 0x64,
472 0xF9, 0x00, 0x00,
473 0x18, 0x65,
474 0xFB, 0x40, 0x09, 0x21, 0xFB, 0x4D, 0x12, 0xD8, 0x4A,
475 0x65, 0x65, 0x75, 0x6C, 0x65, 0x72,
476 0xFB, 0x40, 0x05, 0xBF, 0x0A, 0x8B, 0x14, 0x57, 0x69,
477 0x18, 0x69,
478 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
479 0x18, 0x66,
480 0xF9, 0x00, 0x00,
481 0x18, 0x67,
482 0xFA, 0x40, 0x49, 0x0F, 0xDA,
483 0x66, 0x65, 0x75, 0x6C, 0x65, 0x72, 0x32,
484 0xFA, 0x40, 0x2D, 0xF8, 0x54,
485 0x18, 0x6A,
486 0xFA, 0x00, 0x00, 0x00, 0x00};
487
488int32_t GeneralFloatEncodeTests()
489{
490 UsefulBuf_MAKE_STACK_UB(OutBuffer, sizeof(spExpectedFloats));
491
492 QCBOREncodeContext EC;
493 QCBOREncode_Init(&EC, OutBuffer);
494 QCBOREncode_OpenArray(&EC);
495
496 QCBOREncode_AddDouble(&EC, 0.0);
497 QCBOREncode_AddDouble(&EC, 3.14);
498 QCBOREncode_AddDoubleNoPreferred(&EC, 0.0);
499 QCBOREncode_AddDoubleNoPreferred(&EC, NAN);
500 QCBOREncode_AddDoubleNoPreferred(&EC, INFINITY);
501
502 QCBOREncode_AddFloat(&EC, 0.0);
503 QCBOREncode_AddFloat(&EC, 3.14f);
504 QCBOREncode_AddFloatNoPreferred(&EC, 0.0f);
505 QCBOREncode_AddFloatNoPreferred(&EC, NAN);
506 QCBOREncode_AddFloatNoPreferred(&EC, INFINITY);
507
508 QCBOREncode_OpenMap(&EC);
509
510 QCBOREncode_AddDoubleToMapN(&EC, 100, 0.0);
511 QCBOREncode_AddDoubleToMapN(&EC, 101, 3.1415926);
512 QCBOREncode_AddDoubleToMap(&EC, "euler", 2.71828182845904523536);
513 QCBOREncode_AddDoubleNoPreferredToMapN(&EC, 105, 0.0);
514
515 QCBOREncode_AddFloatToMapN(&EC, 102, 0.0f);
516 QCBOREncode_AddFloatToMapN(&EC, 103, 3.1415926f);
517 QCBOREncode_AddFloatToMap(&EC, "euler2", 2.71828182845904523536f);
518 QCBOREncode_AddFloatNoPreferredToMapN(&EC, 106, 0.0f);
519
520 QCBOREncode_CloseMap(&EC);
521 QCBOREncode_CloseArray(&EC);
522
523 UsefulBufC Encoded;
524 QCBORError uErr = QCBOREncode_Finish(&EC, &Encoded);
525 if(uErr) {
526 return -1;
527 }
528
529 if(UsefulBuf_Compare(Encoded, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloats))) {
530 return -3;
531 }
532
533 return 0;
534}
535
536
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700537
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700538#ifdef NAN_EXPERIMENT
539/*
540 Code for checking what the double to float cast does with
541 NaNs. Not run as part of tests. Keep it around to
542 be able to check various platforms and CPUs.
543 */
544
545#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
546#define DOUBLE_NUM_EXPONENT_BITS (11)
547#define DOUBLE_NUM_SIGN_BITS (1)
548
549#define DOUBLE_SIGNIFICAND_SHIFT (0)
550#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
551#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
552
553#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
554#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
555#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
556#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
557
558
559static int NaNExperiments() {
560 double dqNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT);
561 double dsNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | 0x01);
562 double dqNaNPayload = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT | 0xf00f);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800563
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700564 float f1 = (float)dqNaN;
565 float f2 = (float)dsNaN;
566 float f3 = (float)dqNaNPayload;
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800567
568
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700569 uint32_t uqNaN = UsefulBufUtil_CopyFloatToUint32((float)dqNaN);
570 uint32_t usNaN = UsefulBufUtil_CopyFloatToUint32((float)dsNaN);
571 uint32_t uqNaNPayload = UsefulBufUtil_CopyFloatToUint32((float)dqNaNPayload);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800572
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700573 // Result of this on x86 is that every NaN is a qNaN. The intel
574 // CVTSD2SS instruction ignores the NaN payload and even converts
575 // a sNaN to a qNaN.
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800576
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700577 return 0;
578}
579#endif
580
Laurence Lundbladeb275cdc2020-07-12 12:34:38 -0700581#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700582
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700583