blob: a66ef044a20008c622180decb1dcb8c824c35b0e [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 Lundbladeb992fdb2020-07-20 22:44:11 -0700308 0xB8, 0x1A, 0x64, 0x7A, 0x65, 0x72, 0x6F, 0xF9, 0x00, 0x00,
309 0x6D, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20,
310 0x7A, 0x65, 0x72, 0x6F, 0xF9, 0x80, 0x00, 0x6A, 0x69, 0x6E,
311 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79, 0xF9, 0x7C,
312 0x00, 0x73, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65,
313 0x20, 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74,
314 0x79, 0xF9, 0xFC, 0x00, 0x63, 0x4E, 0x61, 0x4E, 0xF9, 0x7E,
315 0x00, 0x63, 0x6F, 0x6E, 0x65, 0xF9, 0x3C, 0x00, 0x69, 0x6F,
316 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0xF9, 0x35,
317 0x55, 0x76, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20,
318 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69,
319 0x73, 0x69, 0x6F, 0x6E, 0xF9, 0x7B, 0xFF, 0x78, 0x20, 0x6C,
320 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C,
321 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F,
322 0x6E, 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, 0x6F, 0x6E,
323 0x65, 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33,
324 0x78, 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67,
325 0x65, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65,
326 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0xFA, 0x47, 0x80, 0x00,
327 0x00, 0x77, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74,
328 0x20, 0x68, 0x61, 0x6C, 0x66, 0x20, 0x73, 0x75, 0x62, 0x6E,
329 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xFA, 0x33, 0x80, 0x00, 0x00,
330 0x74, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
331 0x68, 0x61, 0x6C, 0x66, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61,
332 0x6C, 0xF9, 0x04, 0x00, 0x78, 0x19, 0x73, 0x6D, 0x61, 0x6C,
333 0x6C, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x20,
334 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x70, 0x6C, 0x75,
335 0x73, 0xFB, 0x3F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
336 0x75, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
337 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x6D, 0x69, 0x6E,
338 0x75, 0x73, 0xFB, 0x3F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
339 0xFF, 0x75, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74,
340 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x6D, 0x69,
341 0x6E, 0x75, 0x73, 0xFA, 0x38, 0x00, 0x00, 0x00, 0x6E, 0x6C,
342 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69, 0x6E,
343 0x67, 0x6C, 0x65, 0xFA, 0x7F, 0x7F, 0xFF, 0xFF, 0x73, 0x6C,
344 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69, 0x6E,
345 0x67, 0x6C, 0x65, 0x20, 0x70, 0x6C, 0x75, 0x73, 0xFB, 0x47,
346 0xEF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0x73, 0x6C, 0x61,
347 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69, 0x6E, 0x67,
348 0x6C, 0x65, 0x20, 0x70, 0x6C, 0x75, 0x73, 0xFB, 0x47, 0xFF,
349 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x6F, 0x73, 0x6D, 0x61,
350 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69, 0x6E, 0x67,
351 0x6C, 0x65, 0xFA, 0x00, 0x80, 0x00, 0x00, 0x74, 0x73, 0x6D,
352 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69, 0x6E,
353 0x67, 0x6C, 0x65, 0x20, 0x70, 0x6C, 0x75, 0x73, 0xFB, 0x38,
354 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x75, 0x73, 0x6D,
355 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69, 0x6E,
356 0x67, 0x6C, 0x65, 0x20, 0x6D, 0x69, 0x6E, 0x75, 0x73, 0xFB,
357 0x38, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x78, 0x1A,
358 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73,
359 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x6D, 0x69, 0x6E, 0x75,
360 0x73, 0x20, 0x6D, 0x6F, 0x72, 0x65, 0xFB, 0x38, 0x00, 0x00,
361 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF9, 0xC0, 0x00, 0x70,
362 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x70, 0x72, 0x65,
363 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0xFA, 0x4B, 0x80, 0x00,
364 0x00, 0x78, 0x1A, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20,
365 0x77, 0x69, 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x69,
366 0x73, 0x69, 0x6F, 0x6E, 0x20, 0x6C, 0x6F, 0x73, 0x73, 0xFB,
367 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x63,
368 0x66, 0x69, 0x6E
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700369};
370
371
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700372/*
373 Makes a double from a uint64_t by copying the bits, not
374 by converting the value.
375 */
376#define MAKE_DOUBLE(x) UsefulBufUtil_CopyUint64ToDouble(x)
377
378
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800379int32_t DoubleAsSmallestTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700380{
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700381 UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, sizeof(spExpectedSmallest));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800382
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700383 QCBOREncodeContext EC;
384 QCBOREncode_Init(&EC, EncodedHalfsMem);
385 QCBOREncode_OpenMap(&EC);
Laurence Lundblade067035b2018-11-28 17:35:25 -0800386
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700387 // Many of these are from
388 // https://en.wikipedia.org/wiki/Half-precision_floating-point_format
389 // and
390 // https://en.wikipedia.org/wiki/Single-precision_floating-point_format
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800391
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700392 // 64 # text(4)
393 // 7A65726F # "zero"
394 // F9 0000 # primitive(0)
395 QCBOREncode_AddDoubleToMap(&EC, "zero", 0.00);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700396
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700397 // 64 # text(4)
398 // 7A65726F # "negative zero"
399 // F9 8000 # primitive(0)
400 QCBOREncode_AddDoubleToMap(&EC, "negative zero", -0.00);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800401
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700402 // 6A # text(10)
403 // 696E66696E6974697479 # "infinitity"
404 // F9 7C00 # primitive(31744)
405 QCBOREncode_AddDoubleToMap(&EC, "infinitity", INFINITY);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800406
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700407 // 73 # text(19)
408 // 6E6567617469766520696E66696E6974697479 # "negative infinitity"
409 // F9 FC00 # primitive(64512)
410 QCBOREncode_AddDoubleToMap(&EC, "negative infinitity", -INFINITY);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800411
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700412 // 63 # text(3)
413 // 4E614E # "NaN"
414 // F9 7E00 # primitive(32256)
415 QCBOREncode_AddDoubleToMap(&EC, "NaN", NAN);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800416
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700417 // TODO: test a few NaN variants
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800418
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700419 // 63 # text(3)
420 // 6F6E65 # "one"
421 // F9 3C00 # primitive(15360)
422 QCBOREncode_AddDoubleToMap(&EC, "one", 1.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800423
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700424 // 69 # text(9)
425 // 6F6E65207468697264 # "one third"
426 // F9 3555 # primitive(13653)
427 QCBOREncode_AddDoubleToMap(&EC, "one third", 0.333251953125);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800428
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700429 // 65504.0, converts to the large possible half-precision.
430 QCBOREncode_AddDoubleToMap(&EC, "largest half-precision", 65504.0);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700431
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700432 // 65504.1, the double that has both to large an exponent and too
433 // much precision, so no conversion.
434 QCBOREncode_AddDoubleToMap(&EC, "largest half-precision point one", 65504.1);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800435
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700436 // 65536.0 has an exponent of 16, which is larger than 15, the
437 // largest half-precision exponent. It is the exponent, not
438 // precision loss that prevents conversion to half. It does convert
439 // to single precision.
440 QCBOREncode_AddDoubleToMap(&EC, "too-large half-precision", 65536.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800441
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700442 // 5.9604644775390625E-8, the smallest possible half-precision
443 // subnormal, but digitis are lost converting to half, so this is
444 // output as a double.
445 QCBOREncode_AddDoubleToMap(&EC,
446 "smallest half subnormal",
447 MAKE_DOUBLE(0x3e70000000000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800448
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700449 // 0.00006103515625, the double value that converts to the smallest
450 // possible half-precision normal. which is what should appear in
451 // the output.
452 QCBOREncode_AddDoubleToMap(&EC,
453 "smallest half normal",
454 MAKE_DOUBLE(0x3f10000000000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800455
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700456 // 0.000061035156250000014 ,the double value that is a tiny bit
457 // greater than smallest possible half-precision normal. It will be
458 // output as a double because converting it will reduce precision.
459 QCBOREncode_AddDoubleToMap(&EC,
460 "smallest half normal plus",
461 MAKE_DOUBLE(0x3f10000000000001));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800462
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700463 // 0.000061035156249999993, the double value that is a tiny bit
464 // smaller than the smallest half-precision normal. This will fail
465 // to convert to a half-precision because both the exponent is too
466 // small and the precision is too large for a half-precision.
467 QCBOREncode_AddDoubleToMap(&EC,
468 "smallest normal minus",
469 MAKE_DOUBLE(0x3f0fffffffffffff));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800470
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700471 // 0.000030517578125, the double value that is too small to fit
472 // into a half-precision because the exponent won't fit, not
473 // because precision would be lost. (This would fit into a
474 // half-precision subnormal, but there is no converstion to
475 // that). This ends up encoded as a single-precision.
476 QCBOREncode_AddDoubleToMap(&EC,
477 "smallest normal minus",
478 MAKE_DOUBLE(0x3f00000000000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800479
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700480 // 3.4028234664e38, the value that converts to the largest possible
481 // single-precision.
482 QCBOREncode_AddDoubleToMap(&EC,
483 "largest single",
484 MAKE_DOUBLE(0x47efffffe0000000));
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700485
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700486 // 3.402823466385289E38, sightly larger than the largest possible
487 // possible precision. Conversion fails because precision would be
488 // lost.
489 QCBOREncode_AddDoubleToMap(&EC,
490 "largest single plus",
491 MAKE_DOUBLE(0x47efffffe0000001));
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700492
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700493 // 6.8056469327705772E38, slightly more larger than the largers
494 // possible single precision. Conversion fails because exponent is
495 // too large.
496 QCBOREncode_AddDoubleToMap(&EC,
497 "largest single plus",
498 MAKE_DOUBLE(0x47ffffffe0000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800499
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700500 // 1.1754943508222875E-38, The double value that converts to the
501 // smallest possible single-precision normal
502 QCBOREncode_AddDoubleToMap(&EC,
503 "smallest single",
504 MAKE_DOUBLE(0x3810000000000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800505
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700506 // 1.1754943508222878E-38, double value that is slightly larger
507 // than the smallest single-precision normal. Conversion fails
508 // because of precision
509 QCBOREncode_AddDoubleToMap(&EC,
510 "smallest single plus",
511 MAKE_DOUBLE(0x3810000000000001));
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700512
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700513 // 1.1754943508222874E-38, slightly smaller than the smallest
514 // single-precision normal. Conversion fails because of precsiion
515 QCBOREncode_AddDoubleToMap(&EC,
516 "smallest single minus",
517 MAKE_DOUBLE(0x380fffffffffffff));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800518
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700519 // 5.8774717541114375E-39, slightly smaller than the smallest
520 // single-precision normal. Conversion fails because the exponent
521 // is too small.
522 QCBOREncode_AddDoubleToMap(&EC,
523 "smallest single minus more",
524 MAKE_DOUBLE(0x3800000000000000));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800525
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700526 // Just -2, which converts to a negative half-precision
527 // F9 C000 # primitive(49152)
528 QCBOREncode_AddDoubleToMapN(&EC, 3, -2.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800529
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700530 // 16777216, No precision loss converting to single
531 // FA 4B800000 # primitive(1266679808)
532 QCBOREncode_AddDoubleToMap(&EC, "single precision", 16777216);
533
534 // 16777217, One more than above. Too much precision for a single
535 // so no conversion.
536 QCBOREncode_AddDoubleToMap(&EC, "single with precision loss", 16777217);
537
538 // Just a convenient marker when cutting and pasting encoded CBOR
539 QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
540
541 QCBOREncode_CloseMap(&EC);
542
543 UsefulBufC EncodedHalfs;
544 QCBORError uErr = QCBOREncode_Finish(&EC, &EncodedHalfs);
545 if(uErr) {
546 return -1;
547 }
548
549 if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedSmallest))) {
550 return -3;
551 }
552
553 return 0;
Laurence Lundblade570fab52018-10-13 18:28:27 +0800554}
Laurence Lundblade585127a2020-07-15 03:25:24 -0700555#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700556
557
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700558/*
559[0.0, 3.14, 0.0, NaN, Infinity, 0.0, 3.140000104904175, 0.0, NaN, Infinity,
560 {100: 0.0, 101: 3.1415926, "euler": 2.718281828459045, 105: 0.0,
561 102: 0.0, 103: 3.141592502593994, "euler2": 2.7182817459106445, 106: 0.0}]
562 */
563static const uint8_t spExpectedFloats[] = {
564 0x8B,
565 0xF9, 0x00, 0x00,
566 0xFB, 0x40, 0x09, 0x1E, 0xB8, 0x51, 0xEB, 0x85, 0x1F,
567 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
568 0xFB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
569 0xFB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
570 0xF9, 0x00, 0x00,
571 0xFA, 0x40, 0x48, 0xF5, 0xC3,
572 0xFA, 0x00, 0x00, 0x00, 0x00,
573 0xFA, 0x7F, 0xC0, 0x00, 0x00,
574 0xFA, 0x7F, 0x80, 0x00, 0x00,
575 0xA8,
576 0x18, 0x64,
577 0xF9, 0x00, 0x00,
578 0x18, 0x65,
579 0xFB, 0x40, 0x09, 0x21, 0xFB, 0x4D, 0x12, 0xD8, 0x4A,
580 0x65, 0x65, 0x75, 0x6C, 0x65, 0x72,
581 0xFB, 0x40, 0x05, 0xBF, 0x0A, 0x8B, 0x14, 0x57, 0x69,
582 0x18, 0x69,
583 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
584 0x18, 0x66,
585 0xF9, 0x00, 0x00,
586 0x18, 0x67,
587 0xFA, 0x40, 0x49, 0x0F, 0xDA,
588 0x66, 0x65, 0x75, 0x6C, 0x65, 0x72, 0x32,
589 0xFA, 0x40, 0x2D, 0xF8, 0x54,
590 0x18, 0x6A,
591 0xFA, 0x00, 0x00, 0x00, 0x00};
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700592
593static const uint8_t spExpectedFloatsNoHalf[] = {
Laurence Lundblade585127a2020-07-15 03:25:24 -0700594 0x8B,
595 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
596 0xFB, 0x40, 0x09, 0x1E, 0xB8, 0x51, 0xEB, 0x85, 0x1F,
597 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
598 0xFB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
599 0xFB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
600 0xFA, 0x00, 0x00, 0x00, 0x00,
601 0xFA, 0x40, 0x48, 0xF5, 0xC3,
602 0xFA, 0x00, 0x00, 0x00, 0x00,
603 0xFA, 0x7F, 0xC0, 0x00, 0x00,
604 0xFA, 0x7F, 0x80, 0x00, 0x00,
605 0xA8,
606 0x18, 0x64,
607 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
608 0x18, 0x65,
609 0xFB, 0x40, 0x09, 0x21, 0xFB, 0x4D, 0x12, 0xD8, 0x4A,
610 0x65, 0x65, 0x75, 0x6C, 0x65, 0x72,
611 0xFB, 0x40, 0x05, 0xBF, 0x0A, 0x8B, 0x14, 0x57, 0x69,
612 0x18, 0x69,
613 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
614 0x18, 0x66,
615 0xFA, 0x00, 0x00, 0x00, 0x00,
616 0x18, 0x67,
617 0xFA, 0x40, 0x49, 0x0F, 0xDA,
618 0x66, 0x65, 0x75, 0x6C, 0x65, 0x72, 0x32,
619 0xFA, 0x40, 0x2D, 0xF8, 0x54,
620 0x18, 0x6A,
621 0xFA, 0x00, 0x00, 0x00, 0x00};
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700622
623int32_t GeneralFloatEncodeTests()
624{
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700625 UsefulBufC ExpectedFloats;
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700626#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700627 UsefulBuf_MAKE_STACK_UB(OutBuffer, sizeof(spExpectedFloats));
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700628 ExpectedFloats = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloats);
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700629 (void)spExpectedFloatsNoHalf; // Avoid unused variable error
630#else
631 UsefulBuf_MAKE_STACK_UB(OutBuffer, sizeof(spExpectedFloatsNoHalf));
Laurence Lundbladeb992fdb2020-07-20 22:44:11 -0700632 ExpectedFloats = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloatsNoHalf);
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700633 (void)spExpectedFloats; // Avoid unused variable error
634#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700635
636 QCBOREncodeContext EC;
637 QCBOREncode_Init(&EC, OutBuffer);
638 QCBOREncode_OpenArray(&EC);
639
640 QCBOREncode_AddDouble(&EC, 0.0);
641 QCBOREncode_AddDouble(&EC, 3.14);
642 QCBOREncode_AddDoubleNoPreferred(&EC, 0.0);
643 QCBOREncode_AddDoubleNoPreferred(&EC, NAN);
644 QCBOREncode_AddDoubleNoPreferred(&EC, INFINITY);
645
646 QCBOREncode_AddFloat(&EC, 0.0);
647 QCBOREncode_AddFloat(&EC, 3.14f);
648 QCBOREncode_AddFloatNoPreferred(&EC, 0.0f);
649 QCBOREncode_AddFloatNoPreferred(&EC, NAN);
650 QCBOREncode_AddFloatNoPreferred(&EC, INFINITY);
651
652 QCBOREncode_OpenMap(&EC);
653
654 QCBOREncode_AddDoubleToMapN(&EC, 100, 0.0);
655 QCBOREncode_AddDoubleToMapN(&EC, 101, 3.1415926);
656 QCBOREncode_AddDoubleToMap(&EC, "euler", 2.71828182845904523536);
657 QCBOREncode_AddDoubleNoPreferredToMapN(&EC, 105, 0.0);
658
659 QCBOREncode_AddFloatToMapN(&EC, 102, 0.0f);
660 QCBOREncode_AddFloatToMapN(&EC, 103, 3.1415926f);
661 QCBOREncode_AddFloatToMap(&EC, "euler2", 2.71828182845904523536f);
662 QCBOREncode_AddFloatNoPreferredToMapN(&EC, 106, 0.0f);
663
664 QCBOREncode_CloseMap(&EC);
665 QCBOREncode_CloseArray(&EC);
666
667 UsefulBufC Encoded;
668 QCBORError uErr = QCBOREncode_Finish(&EC, &Encoded);
669 if(uErr) {
Laurence Lundblade585127a2020-07-15 03:25:24 -0700670 return -1;
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700671 }
672
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700673 if(UsefulBuf_Compare(Encoded, ExpectedFloats)) {
Laurence Lundblade585127a2020-07-15 03:25:24 -0700674 return -3;
Laurence Lundblade32f3e622020-07-13 20:35:11 -0700675 }
676
677 return 0;
678}
679
680
Laurence Lundblade02fcf312020-07-17 02:49:46 -0700681#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
682/* returns 0 if equivalent, non-zero if not equivalent */
683static int CHECK_EXPECTED_DOUBLE(double val, double expected)
684{
685 double diff = val - expected;
686
687 diff = fabs(diff);
688
689 if(diff > 0.000001) {
690 return 1;
691 } else {
692 return 0;
693 }
694}
695#endif
696
697
698int32_t GeneralFloatDecodeTests()
699{
700 UsefulBufC TestData = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloats);
701
702 QCBORDecodeContext DC;
703 QCBORDecode_Init(&DC, TestData, 0);
704
705 QCBORItem Item;
706 QCBORError uErr;
707
708 QCBORDecode_GetNext(&DC, &Item);
709 if(Item.uDataType != QCBOR_TYPE_ARRAY) {
710 return -1;
711 }
712
713#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
714 uErr = QCBORDecode_GetNext(&DC, &Item);
715 if(uErr != QCBOR_SUCCESS ||
716 Item.uDataType != QCBOR_TYPE_DOUBLE ||
717 Item.val.dfnum != 0.0) {
718 return -2;
719 }
720#else
721 uErr = QCBORDecode_GetNext(&DC, &Item);
722 if(uErr != QCBOR_ERR_HALF_PRECISION_UNSUPPORTED) {
723 return -3;
724 }
725#endif
726
727 uErr = QCBORDecode_GetNext(&DC, &Item);
728 if(uErr != QCBOR_SUCCESS ||
729 Item.uDataType != QCBOR_TYPE_DOUBLE ||
730 Item.val.dfnum != 3.14) {
731 return -4;
732 }
733
734 uErr = QCBORDecode_GetNext(&DC, &Item);
735 if(uErr != QCBOR_SUCCESS ||
736 Item.uDataType != QCBOR_TYPE_DOUBLE ||
737 Item.val.dfnum != 0.0) {
738 return -5;
739 }
740
741 uErr = QCBORDecode_GetNext(&DC, &Item);
742 if(uErr != QCBOR_SUCCESS ||
743 Item.uDataType != QCBOR_TYPE_DOUBLE ||
744 !isnan(Item.val.dfnum)) {
745 return -6;
746 }
747
748 uErr = QCBORDecode_GetNext(&DC, &Item);
749 if(uErr != QCBOR_SUCCESS ||
750 Item.uDataType != QCBOR_TYPE_DOUBLE ||
751 Item.val.dfnum != INFINITY) {
752 return -7;
753 }
754
755#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
756 uErr = QCBORDecode_GetNext(&DC, &Item);
757 if(uErr != QCBOR_SUCCESS ||
758 Item.uDataType != QCBOR_TYPE_DOUBLE ||
759 Item.val.dfnum != 0.0) {
760 return -8;
761 }
762
763 uErr = QCBORDecode_GetNext(&DC, &Item);
764 if(uErr != QCBOR_SUCCESS ||
765 Item.uDataType != QCBOR_TYPE_DOUBLE ||
766 CHECK_EXPECTED_DOUBLE(3.14, Item.val.dfnum)) {
767 return -9;
768 }
769
770 uErr = QCBORDecode_GetNext(&DC, &Item);
771 if(uErr != QCBOR_SUCCESS ||
772 Item.uDataType != QCBOR_TYPE_DOUBLE ||
773 Item.val.dfnum != 0.0) {
774 return -10;
775 }
776
777 uErr = QCBORDecode_GetNext(&DC, &Item);
778 if(uErr != QCBOR_SUCCESS ||
779 Item.uDataType != QCBOR_TYPE_DOUBLE ||
780 !isnan(Item.val.dfnum)) {
781 return -11;
782 }
783
784 uErr = QCBORDecode_GetNext(&DC, &Item);
785 if(uErr != QCBOR_SUCCESS ||
786 Item.uDataType != QCBOR_TYPE_DOUBLE ||
787 Item.val.dfnum != INFINITY) {
788 return -12;
789 }
790
791#else
792 uErr = QCBORDecode_GetNext(&DC, &Item);
793 if(uErr != QCBOR_ERR_HALF_PRECISION_UNSUPPORTED) {
794 return -13;
795 }
796
797 uErr = QCBORDecode_GetNext(&DC, &Item);
798 if(uErr != QCBOR_SUCCESS ||
799 Item.uDataType != QCBOR_TYPE_FLOAT ||
800 Item.val.fnum != 3.14f) {
801 return -14;
802 }
803
804 uErr = QCBORDecode_GetNext(&DC, &Item);
805 if(uErr != QCBOR_SUCCESS ||
806 Item.uDataType != QCBOR_TYPE_FLOAT ||
807 Item.val.fnum != 0.0f) {
808 return -15;
809 }
810
811 uErr = QCBORDecode_GetNext(&DC, &Item);
812 if(uErr != QCBOR_SUCCESS ||
813 Item.uDataType != QCBOR_TYPE_FLOAT ||
814 !isnan(Item.val.fnum)) {
815 return -16;
816 }
817
818 uErr = QCBORDecode_GetNext(&DC, &Item);
819 if(uErr != QCBOR_SUCCESS ||
820 Item.uDataType != QCBOR_TYPE_FLOAT ||
821 Item.val.fnum != INFINITY) {
822 return -17;
823 }
824#endif
825 /* Sufficent test coverage. Don't need to decode the rest */
826
827 return 0;
828}
829
830
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700831
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700832#ifdef NAN_EXPERIMENT
833/*
834 Code for checking what the double to float cast does with
835 NaNs. Not run as part of tests. Keep it around to
836 be able to check various platforms and CPUs.
837 */
838
839#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
840#define DOUBLE_NUM_EXPONENT_BITS (11)
841#define DOUBLE_NUM_SIGN_BITS (1)
842
843#define DOUBLE_SIGNIFICAND_SHIFT (0)
844#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
845#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
846
847#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
848#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
849#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
850#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
851
852
853static int NaNExperiments() {
854 double dqNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT);
855 double dsNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | 0x01);
856 double dqNaNPayload = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT | 0xf00f);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800857
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700858 float f1 = (float)dqNaN;
859 float f2 = (float)dsNaN;
860 float f3 = (float)dqNaNPayload;
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800861
862
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700863 uint32_t uqNaN = UsefulBufUtil_CopyFloatToUint32((float)dqNaN);
864 uint32_t usNaN = UsefulBufUtil_CopyFloatToUint32((float)dsNaN);
865 uint32_t uqNaNPayload = UsefulBufUtil_CopyFloatToUint32((float)dqNaNPayload);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800866
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700867 // Result of this on x86 is that every NaN is a qNaN. The intel
868 // CVTSD2SS instruction ignores the NaN payload and even converts
869 // a sNaN to a qNaN.
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800870
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700871 return 0;
872}
873#endif
874
875
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700876