blob: 5aefaea4bd52679d01d33b1a117dc3696de66889 [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 Lundbladebb474be2018-10-22 11:53:21 +0530306static const uint8_t spExpectedSmallest[] = {
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700307 0xB8, 0x1A,
308 0x64, 0x7A, 0x65, 0x72, 0x6F,
309 0xF9, 0x00, 0x00,
310
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700311 0x6D, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x7A,
312 0x65, 0x72, 0x6F,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700313 0xF9, 0x80, 0x00,
314
315 0x6A, 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
316 0xF9, 0x7C, 0x00,
317
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700318 0x73, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69,
319 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700320 0xF9, 0xFC, 0x00,
321
322 0x63, 0x4E, 0x61, 0x4E,
323 0xF9, 0x7E, 0x00,
324
325 0x63, 0x6F, 0x6E, 0x65,
326 0xF9, 0x3C, 0x00,
327
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700328 0x69, 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700329 0xF9, 0x35, 0x55,
330
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700331 0x76, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61,
332 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69,
333 0x6F, 0x6E,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700334 0xF9, 0x7B, 0xFF,
335
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700336 0x78, 0x20, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68,
337 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73,
338 0x69, 0x6F, 0x6E, 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20,
339 0x6F, 0x6E, 0x65,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700340 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33,
341
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700342 0x78, 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65,
343 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63,
344 0x69, 0x73, 0x69, 0x6F, 0x6E,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700345 0xFA, 0x47, 0x80, 0x00, 0x00,
346
347 0x77, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74,
348 0x20, 0x68, 0x61, 0x6C, 0x66, 0x20, 0x73, 0x75, 0x62, 0x6E,
349 0x6F, 0x72, 0x6D, 0x61, 0x6C,
350 0xFA, 0x33, 0x80, 0x00, 0x00,
351
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700352 0x74, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x68,
353 0x61, 0x6C, 0x66, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700354 0xF9, 0x04, 0x00,
355
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700356 0x78, 0x19, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
357 0x68, 0x61, 0x6C, 0x66, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61,
358 0x6C, 0x20, 0x70, 0x6C, 0x75, 0x73,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700359 0xFB, 0x3F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
360
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700361 0x75, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E,
362 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x6D, 0x69, 0x6E,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700363 0x75, 0x73,
364 0xFB, 0x3F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
365
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700366 0x75, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E,
367 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x6D, 0x69, 0x6E, 0x75,
368 0x73,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700369 0xFA, 0x38, 0x00, 0x00, 0x00,
370
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700371 0x6E, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69,
372 0x6E, 0x67, 0x6C, 0x65,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700373 0xFA, 0x7F, 0x7F, 0xFF, 0xFF,
374
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700375 0x73, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69,
376 0x6E,0x67, 0x6C, 0x65, 0x20, 0x70, 0x6C, 0x75, 0x73,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700377 0xFB, 0x47, 0xEF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x01,
378
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700379 0x73, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69,
380 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x70, 0x6C, 0x75, 0x73,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700381 0xFB, 0x47, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
382
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700383 0x6F, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73,
384 0x69, 0x6E, 0x67, 0x6C, 0x65,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700385 0xFA, 0x00, 0x80, 0x00, 0x00,
386
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700387 0x74, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73,
388 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x70, 0x6C, 0x75, 0x73,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700389 0xFB, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
390
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700391 0x75, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73,
392 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x6D, 0x69, 0x6E, 0x75,
393 0x73,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700394 0xFB, 0x38, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
395
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700396 0x78, 0x1A, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
397 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x6D, 0x69, 0x6E,
398 0x75, 0x73, 0x20, 0x6D, 0x6F, 0x72, 0x65,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700399 0xFB, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
400
401 0x03,
402 0xF9, 0xC0, 0x00,
403
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700404 0x70, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x70, 0x72, 0x65,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700405 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
406 0xFA, 0x4B, 0x80, 0x00, 0x00,
407
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700408 0x78, 0x1A, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77, 0x69,
409 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69,
410 0x6F, 0x6E, 0x20, 0x6C, 0x6F, 0x73, 0x73,
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700411 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
412
413 0x01,
414 0x63, 0x66, 0x69, 0x6E
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700415};
416
417
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700418/*
419 Makes a double from a uint64_t by copying the bits, not
420 by converting the value.
421 */
422#define MAKE_DOUBLE(x) UsefulBufUtil_CopyUint64ToDouble(x)
423
424
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800425int32_t DoubleAsSmallestTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700426{
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700427 UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, sizeof(spExpectedSmallest));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800428
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700429 QCBOREncodeContext EC;
430 QCBOREncode_Init(&EC, EncodedHalfsMem);
431 QCBOREncode_OpenMap(&EC);
Laurence Lundblade067035b2018-11-28 17:35:25 -0800432
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700433 // Many of these are from
434 // https://en.wikipedia.org/wiki/Half-precision_floating-point_format
435 // and
436 // https://en.wikipedia.org/wiki/Single-precision_floating-point_format
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800437
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700438 // F9 0000 # primitive(0)
439 QCBOREncode_AddDoubleToMap(&EC, "zero", 0.00);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700440
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700441 // F9 8000 # primitive(0)
442 QCBOREncode_AddDoubleToMap(&EC, "negative zero", -0.00);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800443
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700444 // F9 7C00 # primitive(31744)
445 QCBOREncode_AddDoubleToMap(&EC, "infinitity", INFINITY);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800446
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700447 // F9 FC00 # primitive(64512)
448 QCBOREncode_AddDoubleToMap(&EC, "negative infinitity", -INFINITY);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800449
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700450 // F9 7E00 # primitive(32256)
451 QCBOREncode_AddDoubleToMap(&EC, "NaN", NAN);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800452
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700453 // TODO: test a few NaN variants
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800454
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700455 // F9 3C00 # primitive(15360)
456 QCBOREncode_AddDoubleToMap(&EC, "one", 1.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800457
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700458 // F9 3555 # primitive(13653)
459 QCBOREncode_AddDoubleToMap(&EC, "one third", 0.333251953125);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800460
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700461 // 65504.0, converts to the large possible half-precision.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700462 // 0xF9, 0x7B, 0xFF,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700463 QCBOREncode_AddDoubleToMap(&EC, "largest half-precision", 65504.0);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700464
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700465 // 65504.1, the double that has both to large an exponent and too
466 // much precision, so no conversion.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700467 // 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700468 QCBOREncode_AddDoubleToMap(&EC, "largest half-precision point one", 65504.1);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800469
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700470 // 65536.0 has an exponent of 16, which is larger than 15, the
471 // largest half-precision exponent. It is the exponent, not
472 // precision loss that prevents conversion to half. It does convert
473 // to single precision.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700474 // 0xFA, 0x47, 0x80, 0x00, 0x00,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700475 QCBOREncode_AddDoubleToMap(&EC, "too-large half-precision", 65536.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800476
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700477 // 5.9604644775390625E-8, the smallest possible half-precision
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700478 // subnormal, digitis are lost converting to half, but not
479 // when converting to a single
480 // 0xFA, 0x33, 0x80, 0x00, 0x00,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700481 QCBOREncode_AddDoubleToMap(&EC,
482 "smallest half subnormal",
483 MAKE_DOUBLE(0x3e70000000000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800484
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700485 // 0.00006103515625, the double value that converts to the smallest
486 // possible half-precision normal. which is what should appear in
487 // the output.
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700488 // 0xF9, 0x04, 0x00,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700489 QCBOREncode_AddDoubleToMap(&EC,
490 "smallest half normal",
491 MAKE_DOUBLE(0x3f10000000000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800492
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700493 // 0.000061035156250000014 ,the double value that is a tiny bit
494 // greater than smallest possible half-precision normal. It will be
495 // output as a double because converting it will reduce precision.
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700496 // 0xFB, 0x3F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700497 QCBOREncode_AddDoubleToMap(&EC,
498 "smallest half normal plus",
499 MAKE_DOUBLE(0x3f10000000000001));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800500
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700501 // 0.000061035156249999993, the double value that is a tiny bit
502 // smaller than the smallest half-precision normal. This will fail
503 // to convert to a half-precision because both the exponent is too
504 // small and the precision is too large for a half-precision.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700505 // 0xFB, 0x3F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700506 QCBOREncode_AddDoubleToMap(&EC,
507 "smallest normal minus",
508 MAKE_DOUBLE(0x3f0fffffffffffff));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800509
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700510 // 0.000030517578125, the double value that is too small to fit
511 // into a half-precision because the exponent won't fit, not
512 // because precision would be lost. (This would fit into a
513 // half-precision subnormal, but there is no converstion to
514 // that). This ends up encoded as a single-precision.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700515 // 0xFA, 0x38, 0x00, 0x00, 0x00,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700516 QCBOREncode_AddDoubleToMap(&EC,
517 "smallest normal minus",
518 MAKE_DOUBLE(0x3f00000000000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800519
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700520 // 3.4028234664e38, the value that converts to the largest possible
521 // single-precision.
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700522 // 0xFA, 0x7F, 0x7F, 0xFF, 0xFF,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700523 QCBOREncode_AddDoubleToMap(&EC,
524 "largest single",
525 MAKE_DOUBLE(0x47efffffe0000000));
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700526
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700527 // 3.402823466385289E38, sightly larger than the largest possible
528 // possible precision. Conversion fails because precision would be
529 // lost.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700530 // 0xFB, 0x47, 0xEF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x01,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700531 QCBOREncode_AddDoubleToMap(&EC,
532 "largest single plus",
533 MAKE_DOUBLE(0x47efffffe0000001));
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700534
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700535 // 6.8056469327705772E38, slightly more larger than the largers
536 // possible single precision. Conversion fails because exponent is
537 // too large.
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700538 // 0xFB, 0x47, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700539 QCBOREncode_AddDoubleToMap(&EC,
540 "largest single plus",
541 MAKE_DOUBLE(0x47ffffffe0000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800542
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700543 // 1.1754943508222875E-38, The double value that converts to the
544 // smallest possible single-precision normal
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700545 // 0xFA, 0x00, 0x80, 0x00, 0x00,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700546 QCBOREncode_AddDoubleToMap(&EC,
547 "smallest single",
548 MAKE_DOUBLE(0x3810000000000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800549
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700550 // 1.1754943508222878E-38, double value that is slightly larger
551 // than the smallest single-precision normal. Conversion fails
552 // because of precision
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700553 // 0xFB, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700554 QCBOREncode_AddDoubleToMap(&EC,
555 "smallest single plus",
556 MAKE_DOUBLE(0x3810000000000001));
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700557
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700558 // 1.1754943508222874E-38, slightly smaller than the smallest
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700559 // single-precision normal. Conversion fails because of precision
Laurence Lundblade29ec4642020-07-21 21:11:45 -0700560 // 0xFB, 0x38, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700561 QCBOREncode_AddDoubleToMap(&EC,
562 "smallest single minus",
563 MAKE_DOUBLE(0x380fffffffffffff));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800564
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700565 // 5.8774717541114375E-39, slightly smaller than the smallest
566 // single-precision normal. Conversion fails because the exponent
567 // is too small.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700568 // 0xFB, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700569 QCBOREncode_AddDoubleToMap(&EC,
570 "smallest single minus more",
571 MAKE_DOUBLE(0x3800000000000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800572
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700573 // Just -2, which converts to a negative half-precision
574 // F9 C000 # primitive(49152)
575 QCBOREncode_AddDoubleToMapN(&EC, 3, -2.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800576
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700577 // 16777216, No precision loss converting to single
578 // FA 4B800000 # primitive(1266679808)
579 QCBOREncode_AddDoubleToMap(&EC, "single precision", 16777216);
580
581 // 16777217, One more than above. Too much precision for a single
582 // so no conversion.
Laurence Lundblade60f30e72020-07-21 11:16:01 -0700583 // 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700584 QCBOREncode_AddDoubleToMap(&EC, "single with precision loss", 16777217);
585
586 // Just a convenient marker when cutting and pasting encoded CBOR
587 QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
588
589 QCBOREncode_CloseMap(&EC);
590
591 UsefulBufC EncodedHalfs;
592 QCBORError uErr = QCBOREncode_Finish(&EC, &EncodedHalfs);
593 if(uErr) {
594 return -1;
595 }
596
597 if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedSmallest))) {
598 return -3;
599 }
600
601 return 0;
Laurence Lundblade570fab52018-10-13 18:28:27 +0800602}
Laurence Lundblade585127a2020-07-15 03:25:24 -0700603#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700604
605
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700606/*
607[0.0, 3.14, 0.0, NaN, Infinity, 0.0, 3.140000104904175, 0.0, NaN, Infinity,
608 {100: 0.0, 101: 3.1415926, "euler": 2.718281828459045, 105: 0.0,
609 102: 0.0, 103: 3.141592502593994, "euler2": 2.7182817459106445, 106: 0.0}]
610 */
611static const uint8_t spExpectedFloats[] = {
612 0x8B,
613 0xF9, 0x00, 0x00,
614 0xFB, 0x40, 0x09, 0x1E, 0xB8, 0x51, 0xEB, 0x85, 0x1F,
615 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
616 0xFB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
617 0xFB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
618 0xF9, 0x00, 0x00,
619 0xFA, 0x40, 0x48, 0xF5, 0xC3,
620 0xFA, 0x00, 0x00, 0x00, 0x00,
621 0xFA, 0x7F, 0xC0, 0x00, 0x00,
622 0xFA, 0x7F, 0x80, 0x00, 0x00,
623 0xA8,
624 0x18, 0x64,
625 0xF9, 0x00, 0x00,
626 0x18, 0x65,
627 0xFB, 0x40, 0x09, 0x21, 0xFB, 0x4D, 0x12, 0xD8, 0x4A,
628 0x65, 0x65, 0x75, 0x6C, 0x65, 0x72,
629 0xFB, 0x40, 0x05, 0xBF, 0x0A, 0x8B, 0x14, 0x57, 0x69,
630 0x18, 0x69,
631 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
632 0x18, 0x66,
633 0xF9, 0x00, 0x00,
634 0x18, 0x67,
635 0xFA, 0x40, 0x49, 0x0F, 0xDA,
636 0x66, 0x65, 0x75, 0x6C, 0x65, 0x72, 0x32,
637 0xFA, 0x40, 0x2D, 0xF8, 0x54,
638 0x18, 0x6A,
639 0xFA, 0x00, 0x00, 0x00, 0x00};
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700640
641static const uint8_t spExpectedFloatsNoHalf[] = {
Laurence Lundblade585127a2020-07-15 03:25:24 -0700642 0x8B,
643 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
644 0xFB, 0x40, 0x09, 0x1E, 0xB8, 0x51, 0xEB, 0x85, 0x1F,
645 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
646 0xFB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
647 0xFB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
648 0xFA, 0x00, 0x00, 0x00, 0x00,
649 0xFA, 0x40, 0x48, 0xF5, 0xC3,
650 0xFA, 0x00, 0x00, 0x00, 0x00,
651 0xFA, 0x7F, 0xC0, 0x00, 0x00,
652 0xFA, 0x7F, 0x80, 0x00, 0x00,
653 0xA8,
654 0x18, 0x64,
655 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
656 0x18, 0x65,
657 0xFB, 0x40, 0x09, 0x21, 0xFB, 0x4D, 0x12, 0xD8, 0x4A,
658 0x65, 0x65, 0x75, 0x6C, 0x65, 0x72,
659 0xFB, 0x40, 0x05, 0xBF, 0x0A, 0x8B, 0x14, 0x57, 0x69,
660 0x18, 0x69,
661 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
662 0x18, 0x66,
663 0xFA, 0x00, 0x00, 0x00, 0x00,
664 0x18, 0x67,
665 0xFA, 0x40, 0x49, 0x0F, 0xDA,
666 0x66, 0x65, 0x75, 0x6C, 0x65, 0x72, 0x32,
667 0xFA, 0x40, 0x2D, 0xF8, 0x54,
668 0x18, 0x6A,
669 0xFA, 0x00, 0x00, 0x00, 0x00};
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700670
671int32_t GeneralFloatEncodeTests()
672{
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700673 UsefulBufC ExpectedFloats;
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700674#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700675 UsefulBuf_MAKE_STACK_UB(OutBuffer, sizeof(spExpectedFloats));
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700676 ExpectedFloats = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloats);
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700677 (void)spExpectedFloatsNoHalf; // Avoid unused variable error
678#else
679 UsefulBuf_MAKE_STACK_UB(OutBuffer, sizeof(spExpectedFloatsNoHalf));
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700680 ExpectedFloats = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloatsNoHalf);
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700681 (void)spExpectedFloats; // Avoid unused variable error
682#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700683
684 QCBOREncodeContext EC;
685 QCBOREncode_Init(&EC, OutBuffer);
686 QCBOREncode_OpenArray(&EC);
687
688 QCBOREncode_AddDouble(&EC, 0.0);
689 QCBOREncode_AddDouble(&EC, 3.14);
690 QCBOREncode_AddDoubleNoPreferred(&EC, 0.0);
691 QCBOREncode_AddDoubleNoPreferred(&EC, NAN);
692 QCBOREncode_AddDoubleNoPreferred(&EC, INFINITY);
693
694 QCBOREncode_AddFloat(&EC, 0.0);
695 QCBOREncode_AddFloat(&EC, 3.14f);
696 QCBOREncode_AddFloatNoPreferred(&EC, 0.0f);
697 QCBOREncode_AddFloatNoPreferred(&EC, NAN);
698 QCBOREncode_AddFloatNoPreferred(&EC, INFINITY);
699
700 QCBOREncode_OpenMap(&EC);
701
702 QCBOREncode_AddDoubleToMapN(&EC, 100, 0.0);
703 QCBOREncode_AddDoubleToMapN(&EC, 101, 3.1415926);
704 QCBOREncode_AddDoubleToMap(&EC, "euler", 2.71828182845904523536);
705 QCBOREncode_AddDoubleNoPreferredToMapN(&EC, 105, 0.0);
706
707 QCBOREncode_AddFloatToMapN(&EC, 102, 0.0f);
708 QCBOREncode_AddFloatToMapN(&EC, 103, 3.1415926f);
709 QCBOREncode_AddFloatToMap(&EC, "euler2", 2.71828182845904523536f);
710 QCBOREncode_AddFloatNoPreferredToMapN(&EC, 106, 0.0f);
711
712 QCBOREncode_CloseMap(&EC);
713 QCBOREncode_CloseArray(&EC);
714
715 UsefulBufC Encoded;
716 QCBORError uErr = QCBOREncode_Finish(&EC, &Encoded);
717 if(uErr) {
Laurence Lundblade585127a2020-07-15 03:25:24 -0700718 return -1;
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700719 }
720
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700721 if(UsefulBuf_Compare(Encoded, ExpectedFloats)) {
Laurence Lundblade585127a2020-07-15 03:25:24 -0700722 return -3;
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700723 }
724
725 return 0;
726}
727
728
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700729#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
730/* returns 0 if equivalent, non-zero if not equivalent */
731static int CHECK_EXPECTED_DOUBLE(double val, double expected)
732{
733 double diff = val - expected;
734
735 diff = fabs(diff);
736
737 if(diff > 0.000001) {
738 return 1;
739 } else {
740 return 0;
741 }
742}
743#endif
744
745
746int32_t GeneralFloatDecodeTests()
747{
748 UsefulBufC TestData = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloats);
749
750 QCBORDecodeContext DC;
751 QCBORDecode_Init(&DC, TestData, 0);
752
753 QCBORItem Item;
754 QCBORError uErr;
755
756 QCBORDecode_GetNext(&DC, &Item);
757 if(Item.uDataType != QCBOR_TYPE_ARRAY) {
758 return -1;
759 }
760
761#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
762 uErr = QCBORDecode_GetNext(&DC, &Item);
763 if(uErr != QCBOR_SUCCESS ||
764 Item.uDataType != QCBOR_TYPE_DOUBLE ||
765 Item.val.dfnum != 0.0) {
766 return -2;
767 }
768#else
769 uErr = QCBORDecode_GetNext(&DC, &Item);
770 if(uErr != QCBOR_ERR_HALF_PRECISION_UNSUPPORTED) {
771 return -3;
772 }
773#endif
774
775 uErr = QCBORDecode_GetNext(&DC, &Item);
776 if(uErr != QCBOR_SUCCESS ||
777 Item.uDataType != QCBOR_TYPE_DOUBLE ||
778 Item.val.dfnum != 3.14) {
779 return -4;
780 }
781
782 uErr = QCBORDecode_GetNext(&DC, &Item);
783 if(uErr != QCBOR_SUCCESS ||
784 Item.uDataType != QCBOR_TYPE_DOUBLE ||
785 Item.val.dfnum != 0.0) {
786 return -5;
787 }
788
789 uErr = QCBORDecode_GetNext(&DC, &Item);
790 if(uErr != QCBOR_SUCCESS ||
791 Item.uDataType != QCBOR_TYPE_DOUBLE ||
792 !isnan(Item.val.dfnum)) {
793 return -6;
794 }
795
796 uErr = QCBORDecode_GetNext(&DC, &Item);
797 if(uErr != QCBOR_SUCCESS ||
798 Item.uDataType != QCBOR_TYPE_DOUBLE ||
799 Item.val.dfnum != INFINITY) {
800 return -7;
801 }
802
803#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
804 uErr = QCBORDecode_GetNext(&DC, &Item);
805 if(uErr != QCBOR_SUCCESS ||
806 Item.uDataType != QCBOR_TYPE_DOUBLE ||
807 Item.val.dfnum != 0.0) {
808 return -8;
809 }
810
811 uErr = QCBORDecode_GetNext(&DC, &Item);
812 if(uErr != QCBOR_SUCCESS ||
813 Item.uDataType != QCBOR_TYPE_DOUBLE ||
814 CHECK_EXPECTED_DOUBLE(3.14, Item.val.dfnum)) {
815 return -9;
816 }
817
818 uErr = QCBORDecode_GetNext(&DC, &Item);
819 if(uErr != QCBOR_SUCCESS ||
820 Item.uDataType != QCBOR_TYPE_DOUBLE ||
821 Item.val.dfnum != 0.0) {
822 return -10;
823 }
824
825 uErr = QCBORDecode_GetNext(&DC, &Item);
826 if(uErr != QCBOR_SUCCESS ||
827 Item.uDataType != QCBOR_TYPE_DOUBLE ||
828 !isnan(Item.val.dfnum)) {
829 return -11;
830 }
831
832 uErr = QCBORDecode_GetNext(&DC, &Item);
833 if(uErr != QCBOR_SUCCESS ||
834 Item.uDataType != QCBOR_TYPE_DOUBLE ||
835 Item.val.dfnum != INFINITY) {
836 return -12;
837 }
838
839#else
840 uErr = QCBORDecode_GetNext(&DC, &Item);
841 if(uErr != QCBOR_ERR_HALF_PRECISION_UNSUPPORTED) {
842 return -13;
843 }
844
845 uErr = QCBORDecode_GetNext(&DC, &Item);
846 if(uErr != QCBOR_SUCCESS ||
847 Item.uDataType != QCBOR_TYPE_FLOAT ||
848 Item.val.fnum != 3.14f) {
849 return -14;
850 }
851
852 uErr = QCBORDecode_GetNext(&DC, &Item);
853 if(uErr != QCBOR_SUCCESS ||
854 Item.uDataType != QCBOR_TYPE_FLOAT ||
855 Item.val.fnum != 0.0f) {
856 return -15;
857 }
858
859 uErr = QCBORDecode_GetNext(&DC, &Item);
860 if(uErr != QCBOR_SUCCESS ||
861 Item.uDataType != QCBOR_TYPE_FLOAT ||
862 !isnan(Item.val.fnum)) {
863 return -16;
864 }
865
866 uErr = QCBORDecode_GetNext(&DC, &Item);
867 if(uErr != QCBOR_SUCCESS ||
868 Item.uDataType != QCBOR_TYPE_FLOAT ||
869 Item.val.fnum != INFINITY) {
870 return -17;
871 }
872#endif
873 /* Sufficent test coverage. Don't need to decode the rest */
874
875 return 0;
876}
877
878
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700879
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700880#ifdef NAN_EXPERIMENT
881/*
882 Code for checking what the double to float cast does with
883 NaNs. Not run as part of tests. Keep it around to
884 be able to check various platforms and CPUs.
885 */
886
887#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
888#define DOUBLE_NUM_EXPONENT_BITS (11)
889#define DOUBLE_NUM_SIGN_BITS (1)
890
891#define DOUBLE_SIGNIFICAND_SHIFT (0)
892#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
893#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
894
895#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
896#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
897#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
898#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
899
900
901static int NaNExperiments() {
902 double dqNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT);
903 double dsNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | 0x01);
904 double dqNaNPayload = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT | 0xf00f);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800905
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700906 float f1 = (float)dqNaN;
907 float f2 = (float)dsNaN;
908 float f3 = (float)dqNaNPayload;
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800909
910
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700911 uint32_t uqNaN = UsefulBufUtil_CopyFloatToUint32((float)dqNaN);
912 uint32_t usNaN = UsefulBufUtil_CopyFloatToUint32((float)dsNaN);
913 uint32_t uqNaNPayload = UsefulBufUtil_CopyFloatToUint32((float)dqNaNPayload);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800914
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700915 // Result of this on x86 is that every NaN is a qNaN. The intel
916 // CVTSD2SS instruction ignores the NaN payload and even converts
917 // a sNaN to a qNaN.
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800918
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700919 return 0;
920}
921#endif