blob: c23a3b80b35dee8e00ff0a9ac0bcdabf018e2890 [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 Lundblade2aa0b572020-07-16 19:48:42 -070013
14#include "float_tests.h"
Laurence Lundblade585127a2020-07-15 03:25:24 -070015#include "qcbor/qcbor_encode.h"
Laurence Lundblade02fcf312020-07-17 02:49:46 -070016#include "qcbor/qcbor_decode.h"
Laurence Lundblade585127a2020-07-15 03:25:24 -070017#include <math.h> // For INFINITY and NAN and isnan()
18
Laurence Lundbladeb275cdc2020-07-12 12:34:38 -070019#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
Laurence Lundblade9682a532020-06-06 18:33:04 -070020
Laurence Lundbladed711fb22018-09-26 14:35:22 -070021#include "half_to_double_from_rfc7049.h"
Laurence Lundblade68a13352018-09-23 02:19:54 -070022
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080023
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -070024/*
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -070025 Half-precision values that are input to test half-precision decoding
26
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -070027 As decoded by http://cbor.me
28 {"zero": 0.0,
29 "infinitity": Infinity,
30 "negative infinitity": -Infinity,
31 "NaN": NaN,
32 "one": 1.0,
33 "one third": 0.333251953125,
34 "largest half-precision": 65504.0,
35 "too-large half-precision": Infinity,
36 "smallest subnormal": 5.960464477539063e-8,
37 "smallest normal": 0.00006097555160522461,
38 "biggest subnormal": 0.00006103515625,
39 "subnormal single": 0.0,
40 3: -2.0,
41 4: NaN,
42 5: NaN,
43 6: NaN,
44 7: NaN}
45 */
Laurence Lundbladebb474be2018-10-22 11:53:21 +053046static const uint8_t spExpectedHalf[] = {
Laurence Lundblade7d40d812018-09-30 02:44:01 -070047 0xB1,
Laurence Lundblade68a13352018-09-23 02:19:54 -070048 0x64,
49 0x7A, 0x65, 0x72, 0x6F,
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -070050 0xF9, 0x00, 0x00, // half-precision 0.000
Laurence Lundblade68a13352018-09-23 02:19:54 -070051 0x6A,
52 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -070053 0xF9, 0x7C, 0x00, // Infinity
Laurence Lundblade68a13352018-09-23 02:19:54 -070054 0x73,
Laurence Lundbladeee851742020-01-08 08:37:05 -080055 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6E,
56 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -070057 0xF9, 0xFC, 0x00, // -Inifinity
Laurence Lundblade68a13352018-09-23 02:19:54 -070058 0x63,
59 0x4E, 0x61, 0x4E,
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -070060 0xF9, 0x7E, 0x00, // NaN
Laurence Lundblade68a13352018-09-23 02:19:54 -070061 0x63,
62 0x6F, 0x6E, 0x65,
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -070063 0xF9, 0x3C, 0x00, // 1.0
Laurence Lundblade68a13352018-09-23 02:19:54 -070064 0x69,
65 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -070066 0xF9, 0x35, 0x55, // half-precsion one third 0.333251953125
Laurence Lundblade68a13352018-09-23 02:19:54 -070067 0x76,
Laurence Lundbladeee851742020-01-08 08:37:05 -080068 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C,
69 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -070070 0xF9, 0x7B, 0xFF, // largest half-precision 65504.0
Laurence Lundbladeee851742020-01-08 08:37:05 -080071 0x78, 0x18,
72 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x68,
73 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69,
74 0x6F, 0x6E,
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -070075 0xF9, 0x7C, 0x00, // Infinity
Laurence Lundblade68a13352018-09-23 02:19:54 -070076 0x72,
Laurence Lundbladeee851742020-01-08 08:37:05 -080077 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
78 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -070079 0xF9, 0x00, 0x01, // Smallest half-precision subnormal 0.000000059604645
Laurence Lundblade68a13352018-09-23 02:19:54 -070080 0x71,
Laurence Lundbladeee851742020-01-08 08:37:05 -080081 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62,
82 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -070083 0xF9, 0x03, 0xFF, // Largest half-precision subnormal 0.0000609755516
84 0x6F,
85 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
86 0x72, 0x6D, 0x61, 0x6C,
87 0xF9, 0x04, 0x00, // Smallest half-precision normal 0.000061988
Laurence Lundblade68a13352018-09-23 02:19:54 -070088 0x70,
Laurence Lundbladeee851742020-01-08 08:37:05 -080089 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x73,
90 0x69, 0x6E, 0x67, 0x6C, 0x65,
Laurence Lundblade68a13352018-09-23 02:19:54 -070091 0xF9, 0x00, 0x00,
92 0x03,
Laurence Lundblade7d40d812018-09-30 02:44:01 -070093 0xF9, 0xC0, 0x00, // -2
94 0x04,
95 0xF9, 0x7E, 0x00, // qNaN
96 0x05,
97 0xF9, 0x7C, 0x01, // sNaN
98 0x06,
99 0xF9, 0x7E, 0x0F, // qNaN with payload 0x0f
100 0x07,
101 0xF9, 0x7C, 0x0F, // sNaN with payload 0x0f
Laurence Lundblade68a13352018-09-23 02:19:54 -0700102};
103
104
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700105inline static bool CheckDouble(double d, uint64_t u)
106{
107 return UsefulBufUtil_CopyDoubleToUint64(d) != u;
108}
109
110
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800111int32_t HalfPrecisionDecodeBasicTests()
Laurence Lundblade68a13352018-09-23 02:19:54 -0700112{
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700113 UsefulBufC HalfPrecision = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedHalf);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800114
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700115 QCBORDecodeContext DC;
116 QCBORDecode_Init(&DC, HalfPrecision, 0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800117
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700118 QCBORItem Item;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700119
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700120 QCBORDecode_GetNext(&DC, &Item);
121 if(Item.uDataType != QCBOR_TYPE_MAP) {
122 return -1;
123 }
Laurence Lundblade68a13352018-09-23 02:19:54 -0700124
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700125 QCBORDecode_GetNext(&DC, &Item);
126 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0) {
127 return -2;
128 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800129
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700130 QCBORDecode_GetNext(&DC, &Item);
131 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
132 return -3;
133 }
Laurence Lundblade68a13352018-09-23 02:19:54 -0700134
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700135 QCBORDecode_GetNext(&DC, &Item);
136 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -INFINITY) {
137 return -4;
138 }
Laurence Lundblade68a13352018-09-23 02:19:54 -0700139
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700140 // TODO: NAN-related is this really converting right? It is carrying
141 // payload, but this confuses things.
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700142 QCBORDecode_GetNext(&DC, &Item);
143 if(Item.uDataType != QCBOR_TYPE_DOUBLE || !isnan(Item.val.dfnum)) {
144 return -5;
145 }
Laurence Lundblade68a13352018-09-23 02:19:54 -0700146
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700147 QCBORDecode_GetNext(&DC, &Item);
148 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 1.0) {
149 return -6;
150 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800151
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700152 // Approximately 1/3
153 QCBORDecode_GetNext(&DC, &Item);
154 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.333251953125) {
155 return -7;
156 }
Laurence Lundblade68a13352018-09-23 02:19:54 -0700157
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700158 // Largest half-precision
159 QCBORDecode_GetNext(&DC, &Item);
160 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 65504.0) {
161 return -8;
162 }
Laurence Lundblade68a13352018-09-23 02:19:54 -0700163
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700164 QCBORDecode_GetNext(&DC, &Item);
165 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
166 return -9;
167 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800168
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700169 // Smallest half-precision subnormal
170 QCBORDecode_GetNext(&DC, &Item);
171 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.00000005960464477539063) {
172 return -10;
173 }
Laurence Lundblade68a13352018-09-23 02:19:54 -0700174
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700175 // Largest half-precision subnormal
176 QCBORDecode_GetNext(&DC, &Item);
177 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.00006097555160522461) {
178 return -11;
179 }
Laurence Lundblade68a13352018-09-23 02:19:54 -0700180
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700181 // Smallest half-precision normal
182 QCBORDecode_GetNext(&DC, &Item);
183 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.00006103515625) {
184 return -12;
185 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800186
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700187 // half-precision zero
188 QCBORDecode_GetNext(&DC, &Item);
189 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0) {
190 return -13;
191 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800192
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700193 // negative 2
194 QCBORDecode_GetNext(&DC, &Item);
195 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -2.0) {
196 return -14;
197 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800198
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700199 // TODO: NAN-related double check these four tests
200 QCBORDecode_GetNext(&DC, &Item); // qNaN
201 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700202 CheckDouble(Item.val.dfnum, 0x7ff8000000000000ULL)) {
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700203 return -15;
204 }
205 QCBORDecode_GetNext(&DC, &Item); // sNaN
206 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700207 CheckDouble(Item.val.dfnum, 0x7ff0000000000001ULL)) {
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700208 return -16;
209 }
210 QCBORDecode_GetNext(&DC, &Item); // qNaN with payload 0x0f
211 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700212 CheckDouble(Item.val.dfnum, 0x7ff800000000000fULL)) {
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700213 return -17;
214 }
215 QCBORDecode_GetNext(&DC, &Item); // sNaN with payload 0x0f
216 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700217 CheckDouble(Item.val.dfnum, 0x7ff000000000000fULL)) {
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700218 return -18;
219 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700220
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700221 if(QCBORDecode_Finish(&DC)) {
222 return -19;
223 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800224
Laurence Lundblade6d3f6ec2020-07-18 17:28:26 -0700225 return 0;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700226}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700227
228
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700229
230
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800231int32_t HalfPrecisionAgainstRFCCodeTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700232{
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700233 for(uint32_t uHalfP = 0; uHalfP < 0xffff; uHalfP += 60) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700234 unsigned char x[2];
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800235 x[1] = (uint8_t)(uHalfP & 0xff);
236 x[0] = (uint8_t)(uHalfP >> 8); // uHalfP is always less than 0xffff
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700237 double d = decode_half(x);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800238
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700239 // Contruct the CBOR for the half-precision float by hand
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530240 UsefulBuf_MAKE_STACK_UB(__xx, 3);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700241 UsefulOutBuf UOB;
242 UsefulOutBuf_Init(&UOB, __xx);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800243
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800244 const uint8_t uHalfPrecInitialByte = (uint8_t)(HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5)); // 0xf9
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700245 UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
246 UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
247
Laurence Lundbladeee851742020-01-08 08:37:05 -0800248 // Now parse the hand-constructed CBOR. This will invoke the
249 // conversion to a float
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700250 QCBORDecodeContext DC;
251 QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800252
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700253 QCBORItem Item;
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800254
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700255 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700256 if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700257 return -1;
258 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800259
Laurence Lundbladeee851742020-01-08 08:37:05 -0800260 //printf("%04x QCBOR:%15.15f RFC: %15.15f (%8x)\n",
261 // uHalfP, Item.val.fnum, d , UsefulBufUtil_CopyFloatToUint32(d));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800262
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700263 if(isnan(d)) {
264 // The RFC code uses the native instructions which may or may not
265 // handle sNaN, qNaN and NaN payloads correctly. This test just
266 // makes sure it is a NaN and doesn't worry about the type of NaN
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700267 if(!isnan(Item.val.dfnum)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700268 return -3;
269 }
270 } else {
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700271 if(Item.val.dfnum != d) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700272 return -2;
273 }
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700274 }
275 }
276 return 0;
277}
278
279
280/*
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700281 Expected output from preferred serialization of some of floating-point numbers
282{"zero": 0.0,
283 "negative zero": -0.0,
284 "infinitity": Infinity,
285 "negative infinitity": -Infinity,
286 "NaN": NaN,
287 "one": 1.0,
288 "one third": 0.333251953125,
289 "largest half-precision": 65504.0,
290 "largest half-precision point one": 65504.1,
291 "too-large half-precision": 65536.0,
292 "smallest half subnormal": 5.960464477539063e-8,
293 "smallest half normal": 0.00006103515625,
294 "smallest half normal plus": 0.00006103515625000001,
295 "smallest normal minus": 0.000030517578125,
296 "largest single": 3.4028234663852886e+38,
297 "largest single plus": 6.805646932770577e+38,
298 "smallest single": 1.1754943508222875e-38,
299 "smallest single plus": 1.1754943508222878e-38,
300 "smallest single minus": 1.1754943508222874e-38,
301 "smallest single minus more": 5.877471754111438e-39,
302 3: -2.0, "single precision": 16777216.0,
303 "single with precision loss": 16777217.0,
304 1: "fin"}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700305 */
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700306// TODO: check these values in hex against expected encoded by hand
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530307static const uint8_t spExpectedSmallest[] = {
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700308 0xB8, 0x1A,
309 0x64, 0x7A, 0x65, 0x72, 0x6F,
310 0xF9, 0x00, 0x00,
311
312 0x6D, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20,
313 0x7A, 0x65, 0x72, 0x6F,
314 0xF9, 0x80, 0x00,
315
316 0x6A, 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
317 0xF9, 0x7C, 0x00,
318
319 0x73, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65,
320 0x20, 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74,
321 0x79,
322 0xF9, 0xFC, 0x00,
323
324 0x63, 0x4E, 0x61, 0x4E,
325 0xF9, 0x7E, 0x00,
326
327 0x63, 0x6F, 0x6E, 0x65,
328 0xF9, 0x3C, 0x00,
329
330 0x69, 0x6F,
331 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
332 0xF9, 0x35, 0x55,
333
334 0x76, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20,
335 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69,
336 0x73, 0x69, 0x6F, 0x6E,
337 0xF9, 0x7B, 0xFF,
338
339 0x78, 0x20, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C,
340 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F,
341 0x6E, 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, 0x6F, 0x6E,
342 0x65,
343 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33,
344
345 0x78, 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67,
346 0x65, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65,
347 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
348 0xFA, 0x47, 0x80, 0x00, 0x00,
349
350 0x77, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74,
351 0x20, 0x68, 0x61, 0x6C, 0x66, 0x20, 0x73, 0x75, 0x62, 0x6E,
352 0x6F, 0x72, 0x6D, 0x61, 0x6C,
353 0xFA, 0x33, 0x80, 0x00, 0x00,
354
355 0x74, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
356 0x68, 0x61, 0x6C, 0x66, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61,
357 0x6C,
358 0xF9, 0x04, 0x00,
359
360 0x78, 0x19, 0x73, 0x6D, 0x61, 0x6C,
361 0x6C, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x20,
362 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x70, 0x6C, 0x75,
363 0x73,
364 0xFB, 0x3F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
365
366 0x75, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
367 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x6D, 0x69, 0x6E,
368 0x75, 0x73,
369 0xFB, 0x3F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
370
371 0x75, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74,
372 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x6D, 0x69,
373 0x6E, 0x75, 0x73,
374 0xFA, 0x38, 0x00, 0x00, 0x00,
375
376 0x6E, 0x6C,
377 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69, 0x6E,
378 0x67, 0x6C, 0x65,
379 0xFA, 0x7F, 0x7F, 0xFF, 0xFF,
380
381 0x73, 0x6C,
382 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69, 0x6E,
383 0x67, 0x6C, 0x65, 0x20, 0x70, 0x6C, 0x75, 0x73,
384 0xFB, 0x47, 0xEF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x01,
385
386 0x73, 0x6C, 0x61,
387 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69, 0x6E, 0x67,
388 0x6C, 0x65, 0x20, 0x70, 0x6C, 0x75, 0x73,
389 0xFB, 0x47, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
390
391 0x6F, 0x73, 0x6D, 0x61,
392 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69, 0x6E, 0x67,
393 0x6C, 0x65,
394 0xFA, 0x00, 0x80, 0x00, 0x00,
395
396 0x74, 0x73, 0x6D,
397 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69, 0x6E,
398 0x67, 0x6C, 0x65, 0x20, 0x70, 0x6C, 0x75, 0x73,
399 0xFB, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
400
401 0x75, 0x73, 0x6D,
402 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69, 0x6E,
403 0x67, 0x6C, 0x65, 0x20, 0x6D, 0x69, 0x6E, 0x75, 0x73,
404 0xFB, 0x38, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
405
406 0x78, 0x1A,
407 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73,
408 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x6D, 0x69, 0x6E, 0x75,
409 0x73, 0x20, 0x6D, 0x6F, 0x72, 0x65,
410 0xFB, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
411
412 0x03,
413 0xF9, 0xC0, 0x00,
414
415 0x70,
416 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x70, 0x72, 0x65,
417 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
418 0xFA, 0x4B, 0x80, 0x00, 0x00,
419
420 0x78, 0x1A, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20,
421 0x77, 0x69, 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x69,
422 0x73, 0x69, 0x6F, 0x6E, 0x20, 0x6C, 0x6F, 0x73, 0x73,
423 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
424
425 0x01,
426 0x63, 0x66, 0x69, 0x6E
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700427};
428
429
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700430/*
431 Makes a double from a uint64_t by copying the bits, not
432 by converting the value.
433 */
434#define MAKE_DOUBLE(x) UsefulBufUtil_CopyUint64ToDouble(x)
435
436
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800437int32_t DoubleAsSmallestTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700438{
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700439 UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, sizeof(spExpectedSmallest));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800440
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700441 QCBOREncodeContext EC;
442 QCBOREncode_Init(&EC, EncodedHalfsMem);
443 QCBOREncode_OpenMap(&EC);
Laurence Lundblade067035b2018-11-28 17:35:25 -0800444
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700445 // Many of these are from
446 // https://en.wikipedia.org/wiki/Half-precision_floating-point_format
447 // and
448 // https://en.wikipedia.org/wiki/Single-precision_floating-point_format
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800449
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700450 // 64 # text(4)
451 // 7A65726F # "zero"
452 // F9 0000 # primitive(0)
453 QCBOREncode_AddDoubleToMap(&EC, "zero", 0.00);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700454
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700455 // 64 # text(4)
456 // 7A65726F # "negative zero"
457 // F9 8000 # primitive(0)
458 QCBOREncode_AddDoubleToMap(&EC, "negative zero", -0.00);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800459
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700460 // 6A # text(10)
461 // 696E66696E6974697479 # "infinitity"
462 // F9 7C00 # primitive(31744)
463 QCBOREncode_AddDoubleToMap(&EC, "infinitity", INFINITY);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800464
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700465 // 73 # text(19)
466 // 6E6567617469766520696E66696E6974697479 # "negative infinitity"
467 // F9 FC00 # primitive(64512)
468 QCBOREncode_AddDoubleToMap(&EC, "negative infinitity", -INFINITY);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800469
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700470 // 63 # text(3)
471 // 4E614E # "NaN"
472 // F9 7E00 # primitive(32256)
473 QCBOREncode_AddDoubleToMap(&EC, "NaN", NAN);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800474
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700475 // TODO: test a few NaN variants
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800476
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700477 // 63 # text(3)
478 // 6F6E65 # "one"
479 // F9 3C00 # primitive(15360)
480 QCBOREncode_AddDoubleToMap(&EC, "one", 1.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800481
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700482 // 69 # text(9)
483 // 6F6E65207468697264 # "one third"
484 // F9 3555 # primitive(13653)
485 QCBOREncode_AddDoubleToMap(&EC, "one third", 0.333251953125);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800486
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700487 // 65504.0, converts to the large possible half-precision.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700488 // 0xF9, 0x7B, 0xFF,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700489 QCBOREncode_AddDoubleToMap(&EC, "largest half-precision", 65504.0);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700490
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700491 // 65504.1, the double that has both to large an exponent and too
492 // much precision, so no conversion.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700493 // 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700494 QCBOREncode_AddDoubleToMap(&EC, "largest half-precision point one", 65504.1);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800495
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700496 // 65536.0 has an exponent of 16, which is larger than 15, the
497 // largest half-precision exponent. It is the exponent, not
498 // precision loss that prevents conversion to half. It does convert
499 // to single precision.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700500 // 0xFA, 0x47, 0x80, 0x00, 0x00,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700501 QCBOREncode_AddDoubleToMap(&EC, "too-large half-precision", 65536.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800502
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700503 // 5.9604644775390625E-8, the smallest possible half-precision
504 // subnormal, but digitis are lost converting to half, so this is
505 // output as a double.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700506 // 0xFA, 0x33, 0x80, 0x00, 0x00, TODO: is this right?
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700507 QCBOREncode_AddDoubleToMap(&EC,
508 "smallest half subnormal",
509 MAKE_DOUBLE(0x3e70000000000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800510
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700511 // 0.00006103515625, the double value that converts to the smallest
512 // possible half-precision normal. which is what should appear in
513 // the output.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700514 // 0xF9, 0x04, 0x00,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700515 QCBOREncode_AddDoubleToMap(&EC,
516 "smallest half normal",
517 MAKE_DOUBLE(0x3f10000000000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800518
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700519 // 0.000061035156250000014 ,the double value that is a tiny bit
520 // greater than smallest possible half-precision normal. It will be
521 // output as a double because converting it will reduce precision.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700522 // 0xFB, 0x3F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700523 QCBOREncode_AddDoubleToMap(&EC,
524 "smallest half normal plus",
525 MAKE_DOUBLE(0x3f10000000000001));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800526
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700527 // 0.000061035156249999993, the double value that is a tiny bit
528 // smaller than the smallest half-precision normal. This will fail
529 // to convert to a half-precision because both the exponent is too
530 // small and the precision is too large for a half-precision.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700531 // 0xFB, 0x3F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700532 QCBOREncode_AddDoubleToMap(&EC,
533 "smallest normal minus",
534 MAKE_DOUBLE(0x3f0fffffffffffff));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800535
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700536 // 0.000030517578125, the double value that is too small to fit
537 // into a half-precision because the exponent won't fit, not
538 // because precision would be lost. (This would fit into a
539 // half-precision subnormal, but there is no converstion to
540 // that). This ends up encoded as a single-precision.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700541 // 0xFA, 0x38, 0x00, 0x00, 0x00,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700542 QCBOREncode_AddDoubleToMap(&EC,
543 "smallest normal minus",
544 MAKE_DOUBLE(0x3f00000000000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800545
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700546 // 3.4028234664e38, the value that converts to the largest possible
547 // single-precision.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700548 // 0xFA, 0x7F, 0x7F, 0xFF, 0xFF,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700549 QCBOREncode_AddDoubleToMap(&EC,
550 "largest single",
551 MAKE_DOUBLE(0x47efffffe0000000));
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700552
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700553 // 3.402823466385289E38, sightly larger than the largest possible
554 // possible precision. Conversion fails because precision would be
555 // lost.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700556 // 0xFB, 0x47, 0xEF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x01,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700557 QCBOREncode_AddDoubleToMap(&EC,
558 "largest single plus",
559 MAKE_DOUBLE(0x47efffffe0000001));
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700560
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700561 // 6.8056469327705772E38, slightly more larger than the largers
562 // possible single precision. Conversion fails because exponent is
563 // too large.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700564 // 0xFB, 0x47, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700565 QCBOREncode_AddDoubleToMap(&EC,
566 "largest single plus",
567 MAKE_DOUBLE(0x47ffffffe0000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800568
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700569 // 1.1754943508222875E-38, The double value that converts to the
570 // smallest possible single-precision normal
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700571 // 0xFA, 0x00, 0x80, 0x00, 0x00,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700572 QCBOREncode_AddDoubleToMap(&EC,
573 "smallest single",
574 MAKE_DOUBLE(0x3810000000000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800575
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700576 // 1.1754943508222878E-38, double value that is slightly larger
577 // than the smallest single-precision normal. Conversion fails
578 // because of precision
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700579 // 0xFB, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700580 QCBOREncode_AddDoubleToMap(&EC,
581 "smallest single plus",
582 MAKE_DOUBLE(0x3810000000000001));
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700583
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700584 // 1.1754943508222874E-38, slightly smaller than the smallest
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700585 // single-precision normal. Conversion fails because of precision
586 // 0xFB, 0x38, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700587 QCBOREncode_AddDoubleToMap(&EC,
588 "smallest single minus",
589 MAKE_DOUBLE(0x380fffffffffffff));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800590
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700591 // 5.8774717541114375E-39, slightly smaller than the smallest
592 // single-precision normal. Conversion fails because the exponent
593 // is too small.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700594 // 0xFB, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700595 QCBOREncode_AddDoubleToMap(&EC,
596 "smallest single minus more",
597 MAKE_DOUBLE(0x3800000000000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800598
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700599 // Just -2, which converts to a negative half-precision
600 // F9 C000 # primitive(49152)
601 QCBOREncode_AddDoubleToMapN(&EC, 3, -2.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800602
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700603 // 16777216, No precision loss converting to single
604 // FA 4B800000 # primitive(1266679808)
605 QCBOREncode_AddDoubleToMap(&EC, "single precision", 16777216);
606
607 // 16777217, One more than above. Too much precision for a single
608 // so no conversion.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700609 // 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700610 QCBOREncode_AddDoubleToMap(&EC, "single with precision loss", 16777217);
611
612 // Just a convenient marker when cutting and pasting encoded CBOR
613 QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
614
615 QCBOREncode_CloseMap(&EC);
616
617 UsefulBufC EncodedHalfs;
618 QCBORError uErr = QCBOREncode_Finish(&EC, &EncodedHalfs);
619 if(uErr) {
620 return -1;
621 }
622
623 if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedSmallest))) {
624 return -3;
625 }
626
627 return 0;
Laurence Lundblade570fab52018-10-13 18:28:27 +0800628}
Laurence Lundblade585127a2020-07-15 03:25:24 -0700629#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700630
631
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700632/*
633[0.0, 3.14, 0.0, NaN, Infinity, 0.0, 3.140000104904175, 0.0, NaN, Infinity,
634 {100: 0.0, 101: 3.1415926, "euler": 2.718281828459045, 105: 0.0,
635 102: 0.0, 103: 3.141592502593994, "euler2": 2.7182817459106445, 106: 0.0}]
636 */
637static const uint8_t spExpectedFloats[] = {
638 0x8B,
639 0xF9, 0x00, 0x00,
640 0xFB, 0x40, 0x09, 0x1E, 0xB8, 0x51, 0xEB, 0x85, 0x1F,
641 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
642 0xFB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
643 0xFB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
644 0xF9, 0x00, 0x00,
645 0xFA, 0x40, 0x48, 0xF5, 0xC3,
646 0xFA, 0x00, 0x00, 0x00, 0x00,
647 0xFA, 0x7F, 0xC0, 0x00, 0x00,
648 0xFA, 0x7F, 0x80, 0x00, 0x00,
649 0xA8,
650 0x18, 0x64,
651 0xF9, 0x00, 0x00,
652 0x18, 0x65,
653 0xFB, 0x40, 0x09, 0x21, 0xFB, 0x4D, 0x12, 0xD8, 0x4A,
654 0x65, 0x65, 0x75, 0x6C, 0x65, 0x72,
655 0xFB, 0x40, 0x05, 0xBF, 0x0A, 0x8B, 0x14, 0x57, 0x69,
656 0x18, 0x69,
657 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
658 0x18, 0x66,
659 0xF9, 0x00, 0x00,
660 0x18, 0x67,
661 0xFA, 0x40, 0x49, 0x0F, 0xDA,
662 0x66, 0x65, 0x75, 0x6C, 0x65, 0x72, 0x32,
663 0xFA, 0x40, 0x2D, 0xF8, 0x54,
664 0x18, 0x6A,
665 0xFA, 0x00, 0x00, 0x00, 0x00};
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700666
667static const uint8_t spExpectedFloatsNoHalf[] = {
Laurence Lundblade585127a2020-07-15 03:25:24 -0700668 0x8B,
669 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
670 0xFB, 0x40, 0x09, 0x1E, 0xB8, 0x51, 0xEB, 0x85, 0x1F,
671 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
672 0xFB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
673 0xFB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
674 0xFA, 0x00, 0x00, 0x00, 0x00,
675 0xFA, 0x40, 0x48, 0xF5, 0xC3,
676 0xFA, 0x00, 0x00, 0x00, 0x00,
677 0xFA, 0x7F, 0xC0, 0x00, 0x00,
678 0xFA, 0x7F, 0x80, 0x00, 0x00,
679 0xA8,
680 0x18, 0x64,
681 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
682 0x18, 0x65,
683 0xFB, 0x40, 0x09, 0x21, 0xFB, 0x4D, 0x12, 0xD8, 0x4A,
684 0x65, 0x65, 0x75, 0x6C, 0x65, 0x72,
685 0xFB, 0x40, 0x05, 0xBF, 0x0A, 0x8B, 0x14, 0x57, 0x69,
686 0x18, 0x69,
687 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
688 0x18, 0x66,
689 0xFA, 0x00, 0x00, 0x00, 0x00,
690 0x18, 0x67,
691 0xFA, 0x40, 0x49, 0x0F, 0xDA,
692 0x66, 0x65, 0x75, 0x6C, 0x65, 0x72, 0x32,
693 0xFA, 0x40, 0x2D, 0xF8, 0x54,
694 0x18, 0x6A,
695 0xFA, 0x00, 0x00, 0x00, 0x00};
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700696
697int32_t GeneralFloatEncodeTests()
698{
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700699 UsefulBufC ExpectedFloats;
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700700#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700701 UsefulBuf_MAKE_STACK_UB(OutBuffer, sizeof(spExpectedFloats));
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700702 ExpectedFloats = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloats);
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700703 (void)spExpectedFloatsNoHalf; // Avoid unused variable error
704#else
705 UsefulBuf_MAKE_STACK_UB(OutBuffer, sizeof(spExpectedFloatsNoHalf));
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700706 ExpectedFloats = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloatsNoHalf);
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700707 (void)spExpectedFloats; // Avoid unused variable error
708#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700709
710 QCBOREncodeContext EC;
711 QCBOREncode_Init(&EC, OutBuffer);
712 QCBOREncode_OpenArray(&EC);
713
714 QCBOREncode_AddDouble(&EC, 0.0);
715 QCBOREncode_AddDouble(&EC, 3.14);
716 QCBOREncode_AddDoubleNoPreferred(&EC, 0.0);
717 QCBOREncode_AddDoubleNoPreferred(&EC, NAN);
718 QCBOREncode_AddDoubleNoPreferred(&EC, INFINITY);
719
720 QCBOREncode_AddFloat(&EC, 0.0);
721 QCBOREncode_AddFloat(&EC, 3.14f);
722 QCBOREncode_AddFloatNoPreferred(&EC, 0.0f);
723 QCBOREncode_AddFloatNoPreferred(&EC, NAN);
724 QCBOREncode_AddFloatNoPreferred(&EC, INFINITY);
725
726 QCBOREncode_OpenMap(&EC);
727
728 QCBOREncode_AddDoubleToMapN(&EC, 100, 0.0);
729 QCBOREncode_AddDoubleToMapN(&EC, 101, 3.1415926);
730 QCBOREncode_AddDoubleToMap(&EC, "euler", 2.71828182845904523536);
731 QCBOREncode_AddDoubleNoPreferredToMapN(&EC, 105, 0.0);
732
733 QCBOREncode_AddFloatToMapN(&EC, 102, 0.0f);
734 QCBOREncode_AddFloatToMapN(&EC, 103, 3.1415926f);
735 QCBOREncode_AddFloatToMap(&EC, "euler2", 2.71828182845904523536f);
736 QCBOREncode_AddFloatNoPreferredToMapN(&EC, 106, 0.0f);
737
738 QCBOREncode_CloseMap(&EC);
739 QCBOREncode_CloseArray(&EC);
740
741 UsefulBufC Encoded;
742 QCBORError uErr = QCBOREncode_Finish(&EC, &Encoded);
743 if(uErr) {
Laurence Lundblade585127a2020-07-15 03:25:24 -0700744 return -1;
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700745 }
746
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700747 if(UsefulBuf_Compare(Encoded, ExpectedFloats)) {
Laurence Lundblade585127a2020-07-15 03:25:24 -0700748 return -3;
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700749 }
750
751 return 0;
752}
753
754
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700755#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
756/* returns 0 if equivalent, non-zero if not equivalent */
757static int CHECK_EXPECTED_DOUBLE(double val, double expected)
758{
759 double diff = val - expected;
760
761 diff = fabs(diff);
762
763 if(diff > 0.000001) {
764 return 1;
765 } else {
766 return 0;
767 }
768}
769#endif
770
771
772int32_t GeneralFloatDecodeTests()
773{
774 UsefulBufC TestData = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloats);
775
776 QCBORDecodeContext DC;
777 QCBORDecode_Init(&DC, TestData, 0);
778
779 QCBORItem Item;
780 QCBORError uErr;
781
782 QCBORDecode_GetNext(&DC, &Item);
783 if(Item.uDataType != QCBOR_TYPE_ARRAY) {
784 return -1;
785 }
786
787#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
788 uErr = QCBORDecode_GetNext(&DC, &Item);
789 if(uErr != QCBOR_SUCCESS ||
790 Item.uDataType != QCBOR_TYPE_DOUBLE ||
791 Item.val.dfnum != 0.0) {
792 return -2;
793 }
794#else
795 uErr = QCBORDecode_GetNext(&DC, &Item);
796 if(uErr != QCBOR_ERR_HALF_PRECISION_UNSUPPORTED) {
797 return -3;
798 }
799#endif
800
801 uErr = QCBORDecode_GetNext(&DC, &Item);
802 if(uErr != QCBOR_SUCCESS ||
803 Item.uDataType != QCBOR_TYPE_DOUBLE ||
804 Item.val.dfnum != 3.14) {
805 return -4;
806 }
807
808 uErr = QCBORDecode_GetNext(&DC, &Item);
809 if(uErr != QCBOR_SUCCESS ||
810 Item.uDataType != QCBOR_TYPE_DOUBLE ||
811 Item.val.dfnum != 0.0) {
812 return -5;
813 }
814
815 uErr = QCBORDecode_GetNext(&DC, &Item);
816 if(uErr != QCBOR_SUCCESS ||
817 Item.uDataType != QCBOR_TYPE_DOUBLE ||
818 !isnan(Item.val.dfnum)) {
819 return -6;
820 }
821
822 uErr = QCBORDecode_GetNext(&DC, &Item);
823 if(uErr != QCBOR_SUCCESS ||
824 Item.uDataType != QCBOR_TYPE_DOUBLE ||
825 Item.val.dfnum != INFINITY) {
826 return -7;
827 }
828
829#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
830 uErr = QCBORDecode_GetNext(&DC, &Item);
831 if(uErr != QCBOR_SUCCESS ||
832 Item.uDataType != QCBOR_TYPE_DOUBLE ||
833 Item.val.dfnum != 0.0) {
834 return -8;
835 }
836
837 uErr = QCBORDecode_GetNext(&DC, &Item);
838 if(uErr != QCBOR_SUCCESS ||
839 Item.uDataType != QCBOR_TYPE_DOUBLE ||
840 CHECK_EXPECTED_DOUBLE(3.14, Item.val.dfnum)) {
841 return -9;
842 }
843
844 uErr = QCBORDecode_GetNext(&DC, &Item);
845 if(uErr != QCBOR_SUCCESS ||
846 Item.uDataType != QCBOR_TYPE_DOUBLE ||
847 Item.val.dfnum != 0.0) {
848 return -10;
849 }
850
851 uErr = QCBORDecode_GetNext(&DC, &Item);
852 if(uErr != QCBOR_SUCCESS ||
853 Item.uDataType != QCBOR_TYPE_DOUBLE ||
854 !isnan(Item.val.dfnum)) {
855 return -11;
856 }
857
858 uErr = QCBORDecode_GetNext(&DC, &Item);
859 if(uErr != QCBOR_SUCCESS ||
860 Item.uDataType != QCBOR_TYPE_DOUBLE ||
861 Item.val.dfnum != INFINITY) {
862 return -12;
863 }
864
865#else
866 uErr = QCBORDecode_GetNext(&DC, &Item);
867 if(uErr != QCBOR_ERR_HALF_PRECISION_UNSUPPORTED) {
868 return -13;
869 }
870
871 uErr = QCBORDecode_GetNext(&DC, &Item);
872 if(uErr != QCBOR_SUCCESS ||
873 Item.uDataType != QCBOR_TYPE_FLOAT ||
874 Item.val.fnum != 3.14f) {
875 return -14;
876 }
877
878 uErr = QCBORDecode_GetNext(&DC, &Item);
879 if(uErr != QCBOR_SUCCESS ||
880 Item.uDataType != QCBOR_TYPE_FLOAT ||
881 Item.val.fnum != 0.0f) {
882 return -15;
883 }
884
885 uErr = QCBORDecode_GetNext(&DC, &Item);
886 if(uErr != QCBOR_SUCCESS ||
887 Item.uDataType != QCBOR_TYPE_FLOAT ||
888 !isnan(Item.val.fnum)) {
889 return -16;
890 }
891
892 uErr = QCBORDecode_GetNext(&DC, &Item);
893 if(uErr != QCBOR_SUCCESS ||
894 Item.uDataType != QCBOR_TYPE_FLOAT ||
895 Item.val.fnum != INFINITY) {
896 return -17;
897 }
898#endif
899 /* Sufficent test coverage. Don't need to decode the rest */
900
901 return 0;
902}
903
904
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700905
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700906#ifdef NAN_EXPERIMENT
907/*
908 Code for checking what the double to float cast does with
909 NaNs. Not run as part of tests. Keep it around to
910 be able to check various platforms and CPUs.
911 */
912
913#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
914#define DOUBLE_NUM_EXPONENT_BITS (11)
915#define DOUBLE_NUM_SIGN_BITS (1)
916
917#define DOUBLE_SIGNIFICAND_SHIFT (0)
918#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
919#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
920
921#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
922#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
923#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
924#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
925
926
927static int NaNExperiments() {
928 double dqNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT);
929 double dsNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | 0x01);
930 double dqNaNPayload = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT | 0xf00f);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800931
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700932 float f1 = (float)dqNaN;
933 float f2 = (float)dsNaN;
934 float f3 = (float)dqNaNPayload;
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800935
936
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700937 uint32_t uqNaN = UsefulBufUtil_CopyFloatToUint32((float)dqNaN);
938 uint32_t usNaN = UsefulBufUtil_CopyFloatToUint32((float)dsNaN);
939 uint32_t uqNaNPayload = UsefulBufUtil_CopyFloatToUint32((float)dqNaNPayload);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800940
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700941 // Result of this on x86 is that every NaN is a qNaN. The intel
942 // CVTSD2SS instruction ignores the NaN payload and even converts
943 // a sNaN to a qNaN.
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800944
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700945 return 0;
946}
947#endif
948
949
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700950