blob: 54683c3733bb218020673fb0886305f2814d90ae [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 Lundblade9682a532020-06-06 18:33:04 -070013#ifndef QCBOR_CONFIG_DISABLE_ENCODE_IEEE754
14
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080015#include "float_tests.h"
Laurence Lundblade68a13352018-09-23 02:19:54 -070016#include "qcbor.h"
Laurence Lundbladed711fb22018-09-26 14:35:22 -070017#include "half_to_double_from_rfc7049.h"
18#include <math.h> // For INFINITY and NAN and isnan()
Laurence Lundblade68a13352018-09-23 02:19:54 -070019
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080020
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080021
Laurence Lundbladebb474be2018-10-22 11:53:21 +053022static const uint8_t spExpectedHalf[] = {
Laurence Lundblade7d40d812018-09-30 02:44:01 -070023 0xB1,
Laurence Lundblade68a13352018-09-23 02:19:54 -070024 0x64,
25 0x7A, 0x65, 0x72, 0x6F,
26 0xF9, 0x00, 0x00, // 0.000
27 0x6A,
28 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
29 0xF9, 0x7C, 0x00, // Infinity
30 0x73,
Laurence Lundbladeee851742020-01-08 08:37:05 -080031 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6E,
32 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
Laurence Lundblade68a13352018-09-23 02:19:54 -070033 0xF9, 0xFC, 0x00, // -Inifinity
34 0x63,
35 0x4E, 0x61, 0x4E,
36 0xF9, 0x7E, 0x00, // NaN
37 0x63,
38 0x6F, 0x6E, 0x65,
39 0xF9, 0x3C, 0x00, // 1.0
40 0x69,
41 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
42 0xF9, 0x35, 0x55, // 0.333251953125
43 0x76,
Laurence Lundbladeee851742020-01-08 08:37:05 -080044 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C,
45 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
Laurence Lundblade68a13352018-09-23 02:19:54 -070046 0xF9, 0x7B, 0xFF, // 65504.0
Laurence Lundbladeee851742020-01-08 08:37:05 -080047 0x78, 0x18,
48 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x68,
49 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69,
50 0x6F, 0x6E,
Laurence Lundblade68a13352018-09-23 02:19:54 -070051 0xF9, 0x7C, 0x00, // Infinity
52 0x72,
Laurence Lundbladeee851742020-01-08 08:37:05 -080053 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
54 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
Laurence Lundblade68a13352018-09-23 02:19:54 -070055 0xF9, 0x00, 0x01, // 0.000000059604
56 0x6F,
Laurence Lundbladeee851742020-01-08 08:37:05 -080057 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
58 0x72, 0x6D, 0x61, 0x6C,
Laurence Lundblade68a13352018-09-23 02:19:54 -070059 0xF9, 0x03, 0xFF, // 0.0000609755516
60 0x71,
Laurence Lundbladeee851742020-01-08 08:37:05 -080061 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62,
62 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
Laurence Lundblade68a13352018-09-23 02:19:54 -070063 0xF9, 0x04, 0x00, // 0.000061988
64 0x70,
Laurence Lundbladeee851742020-01-08 08:37:05 -080065 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x73,
66 0x69, 0x6E, 0x67, 0x6C, 0x65,
Laurence Lundblade68a13352018-09-23 02:19:54 -070067 0xF9, 0x00, 0x00,
68 0x03,
Laurence Lundblade7d40d812018-09-30 02:44:01 -070069 0xF9, 0xC0, 0x00, // -2
70 0x04,
71 0xF9, 0x7E, 0x00, // qNaN
72 0x05,
73 0xF9, 0x7C, 0x01, // sNaN
74 0x06,
75 0xF9, 0x7E, 0x0F, // qNaN with payload 0x0f
76 0x07,
77 0xF9, 0x7C, 0x0F, // sNaN with payload 0x0f
Laurence Lundblade3aee3a32018-12-17 16:17:45 -080078
Laurence Lundblade68a13352018-09-23 02:19:54 -070079};
80
81
Laurence Lundbladec5fef682020-01-25 11:38:45 -080082int32_t HalfPrecisionDecodeBasicTests()
Laurence Lundblade68a13352018-09-23 02:19:54 -070083{
Laurence Lundbladebb474be2018-10-22 11:53:21 +053084 UsefulBufC HalfPrecision = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedHalf);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -080085
Laurence Lundblade68a13352018-09-23 02:19:54 -070086 QCBORDecodeContext DC;
87 QCBORDecode_Init(&DC, HalfPrecision, 0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -080088
Laurence Lundblade68a13352018-09-23 02:19:54 -070089 QCBORItem Item;
90
91 QCBORDecode_GetNext(&DC, &Item);
92 if(Item.uDataType != QCBOR_TYPE_MAP) {
93 return -1;
94 }
95
96 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +070097 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -070098 return -2;
Laurence Lundblade68a13352018-09-23 02:19:54 -070099 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800100
Laurence Lundblade68a13352018-09-23 02:19:54 -0700101 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700102 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700103 return -3;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700104 }
105
106 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700107 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700108 return -4;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700109 }
110
Laurence Lundbladeee851742020-01-08 08:37:05 -0800111 // TODO, is this really converting right? It is carrying payload, but
112 // this confuses things.
113 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700114 if(Item.uDataType != QCBOR_TYPE_DOUBLE || !isnan(Item.val.dfnum)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700115 return -5;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700116 }
117
118 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700119 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 1.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700120 return -6;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700121 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800122
Laurence Lundblade68a13352018-09-23 02:19:54 -0700123 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700124 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.333251953125F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700125 return -7;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700126 }
127
128 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700129 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 65504.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700130 return -8;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700131 }
132
133 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700134 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700135 return -9;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700136 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800137
Laurence Lundblade68a13352018-09-23 02:19:54 -0700138 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700139 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000000596046448F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700140 return -10;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700141 }
142
143 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700144 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000609755516F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700145 return -11;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700146 }
147
148 QCBORDecode_GetNext(&DC, &Item); // TODO check this
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700149 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000610351563F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700150 return -12;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700151 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800152
153 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700154 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700155 return -13;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700156 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800157
Laurence Lundblade68a13352018-09-23 02:19:54 -0700158 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700159 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -2.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700160 return -14;
161 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800162
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700163 // TODO: double check these four tests
164 QCBORDecode_GetNext(&DC, &Item); // qNaN
Laurence Lundbladeee851742020-01-08 08:37:05 -0800165 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
166 UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff8000000000000ULL) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700167 return -15;
168 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700169 QCBORDecode_GetNext(&DC, &Item); // sNaN
Laurence Lundbladeee851742020-01-08 08:37:05 -0800170 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
171 UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff0000000000001ULL) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700172 return -16;
173 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700174 QCBORDecode_GetNext(&DC, &Item); // qNaN with payload 0x0f
Laurence Lundbladeee851742020-01-08 08:37:05 -0800175 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
176 UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff800000000000fULL) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700177 return -17;
178 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700179 QCBORDecode_GetNext(&DC, &Item); // sNaN with payload 0x0f
Laurence Lundbladeee851742020-01-08 08:37:05 -0800180 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
181 UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff000000000000fULL) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700182 return -18;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700183 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700184
Laurence Lundblade68a13352018-09-23 02:19:54 -0700185 if(QCBORDecode_Finish(&DC)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700186 return -19;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700187 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800188
Laurence Lundblade68a13352018-09-23 02:19:54 -0700189 return 0;
190}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700191
192
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700193
194
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800195int32_t HalfPrecisionAgainstRFCCodeTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700196{
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700197 for(uint32_t uHalfP = 0; uHalfP < 0xffff; uHalfP += 60) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700198 unsigned char x[2];
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800199 x[1] = (uint8_t)(uHalfP & 0xff);
200 x[0] = (uint8_t)(uHalfP >> 8); // uHalfP is always less than 0xffff
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700201 double d = decode_half(x);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800202
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700203 // Contruct the CBOR for the half-precision float by hand
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530204 UsefulBuf_MAKE_STACK_UB(__xx, 3);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700205 UsefulOutBuf UOB;
206 UsefulOutBuf_Init(&UOB, __xx);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800207
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800208 const uint8_t uHalfPrecInitialByte = (uint8_t)(HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5)); // 0xf9
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700209 UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
210 UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
211
Laurence Lundbladeee851742020-01-08 08:37:05 -0800212 // Now parse the hand-constructed CBOR. This will invoke the
213 // conversion to a float
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700214 QCBORDecodeContext DC;
215 QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800216
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700217 QCBORItem Item;
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800218
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700219 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700220 if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700221 return -1;
222 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800223
Laurence Lundbladeee851742020-01-08 08:37:05 -0800224 //printf("%04x QCBOR:%15.15f RFC: %15.15f (%8x)\n",
225 // uHalfP, Item.val.fnum, d , UsefulBufUtil_CopyFloatToUint32(d));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800226
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700227 if(isnan(d)) {
228 // The RFC code uses the native instructions which may or may not
229 // handle sNaN, qNaN and NaN payloads correctly. This test just
230 // makes sure it is a NaN and doesn't worry about the type of NaN
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700231 if(!isnan(Item.val.dfnum)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700232 return -3;
233 }
234 } else {
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700235 if(Item.val.dfnum != d) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700236 return -2;
237 }
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700238 }
239 }
240 return 0;
241}
242
243
244/*
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530245 {"zero": 0.0,
246 "negative zero": -0.0,
247 "infinitity": Infinity,
248 "negative infinitity": -Infinity,
249 "NaN": NaN,
250 "one": 1.0,
251 "one third": 0.333251953125,
252 "largest half-precision": 65504.0,
253 "largest half-precision point one": 65504.1,
254 "too-large half-precision": 65536.0,
255 "smallest subnormal": 5.96046448e-8,
256 "smallest normal": 0.00006103515261202119,
257 "biggest subnormal": 0.00006103515625,
258 "subnormal single": 4.00000646641519e-40,
259 3: -2.0,
260 "large single exp": 2.5521177519070385e+38,
261 "too-large single exp": 5.104235503814077e+38,
262 "biggest single with prec": 16777216.0,
263 "first single with prec loss": 16777217.0,
264 1: "fin"}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700265 */
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530266static const uint8_t spExpectedSmallest[] = {
267 0xB4, 0x64, 0x7A, 0x65, 0x72, 0x6F, 0xF9, 0x00, 0x00, 0x6D,
268 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x7A,
269 0x65, 0x72, 0x6F, 0xF9, 0x80, 0x00, 0x6A, 0x69, 0x6E, 0x66,
270 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79, 0xF9, 0x7C, 0x00,
271 0x73, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20,
272 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
273 0xF9, 0xFC, 0x00, 0x63, 0x4E, 0x61, 0x4E, 0xF9, 0x7E, 0x00,
274 0x63, 0x6F, 0x6E, 0x65, 0xF9, 0x3C, 0x00, 0x69, 0x6F, 0x6E,
275 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0xF9, 0x35, 0x55,
276 0x76, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68,
277 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73,
278 0x69, 0x6F, 0x6E, 0xF9, 0x7B, 0xFF, 0x78, 0x20, 0x6C, 0x61,
279 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66,
280 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
281 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, 0x6F, 0x6E, 0x65,
282 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33, 0x78,
283 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65,
284 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63,
285 0x69, 0x73, 0x69, 0x6F, 0x6E, 0xFA, 0x47, 0x80, 0x00, 0x00,
286 0x72, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
287 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xFB,
288 0x3E, 0x70, 0x00, 0x00, 0x00, 0x1C, 0x5F, 0x68, 0x6F, 0x73,
289 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
290 0x72, 0x6D, 0x61, 0x6C, 0xFA, 0x38, 0x7F, 0xFF, 0xFF, 0x71,
291 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
292 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xF9, 0x04, 0x00,
293 0x70, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
294 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0xFB, 0x37, 0xC1,
295 0x6C, 0x28, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF9, 0xC0, 0x00,
296 0x70, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E,
297 0x67, 0x6C, 0x65, 0x20, 0x65, 0x78, 0x70, 0xFA, 0x7F, 0x40,
298 0x00, 0x00, 0x74, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72,
299 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20,
300 0x65, 0x78, 0x70, 0xFB, 0x47, 0xF8, 0x00, 0x00, 0x00, 0x00,
301 0x00, 0x00, 0x78, 0x18, 0x62, 0x69, 0x67, 0x67, 0x65, 0x73,
302 0x74, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77,
303 0x69, 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0xFA, 0x4B,
304 0x80, 0x00, 0x00, 0x78, 0x1B, 0x66, 0x69, 0x72, 0x73, 0x74,
305 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77, 0x69,
306 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x20, 0x6C, 0x6F,
307 0x73, 0x73, 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00,
308 0x00, 0x01, 0x63, 0x66, 0x69, 0x6E
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700309};
310
311
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800312int32_t DoubleAsSmallestTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700313{
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530314 UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, 420);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800315
Laurence Lundblade067035b2018-11-28 17:35:25 -0800316#define QCBOREncode_AddDoubleAsSmallestToMap QCBOREncode_AddDoubleToMap
317#define QCBOREncode_AddDoubleAsSmallestToMapN QCBOREncode_AddDoubleToMapN
318
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800319
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700320 QCBOREncodeContext EC;
321 QCBOREncode_Init(&EC, EncodedHalfsMem);
322 // These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
323 QCBOREncode_OpenMap(&EC);
324 // 64 # text(4)
325 // 7A65726F # "zero"
326 // F9 0000 # primitive(0)
327 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "zero", 0.00);
328
329 // 64 # text(4)
330 // 7A65726F # "negative zero"
331 // F9 8000 # primitive(0)
332 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative zero", -0.00);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800333
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700334 // 6A # text(10)
335 // 696E66696E6974697479 # "infinitity"
336 // F9 7C00 # primitive(31744)
337 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "infinitity", INFINITY);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800338
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700339 // 73 # text(19)
340 // 6E6567617469766520696E66696E6974697479 # "negative infinitity"
341 // F9 FC00 # primitive(64512)
342 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative infinitity", -INFINITY);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800343
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700344 // 63 # text(3)
345 // 4E614E # "NaN"
346 // F9 7E00 # primitive(32256)
347 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "NaN", NAN);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800348
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700349 // TODO: test a few NaN variants
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800350
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700351 // 63 # text(3)
352 // 6F6E65 # "one"
353 // F9 3C00 # primitive(15360)
354 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one", 1.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800355
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700356 // 69 # text(9)
357 // 6F6E65207468697264 # "one third"
358 // F9 3555 # primitive(13653)
359 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one third", 0.333251953125);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800360
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700361 // 76 # text(22)
Laurence Lundbladeee851742020-01-08 08:37:05 -0800362 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700363 // F9 7BFF # primitive(31743)
364 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision",65504.0);
365
366 // 76 # text(22)
Laurence Lundbladeee851742020-01-08 08:37:05 -0800367 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700368 // F9 7BFF # primitive(31743)
369 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision point one",65504.1);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800370
Laurence Lundbladeee851742020-01-08 08:37:05 -0800371 // Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which
372 // is larger than 15, the largest half-precision exponent
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700373 // 78 18 # text(24)
374 // 746F6F2D6C617267652068616C662D707265636973696F6E # "too-large half-precision"
375 // FA 47800000 # primitive(31743)
376 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large half-precision", 65536.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800377
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700378 // The smallest possible half-precision subnormal, but digitis are lost converting
379 // to half, so this turns into a double
380 // 72 # text(18)
381 // 736D616C6C657374207375626E6F726D616C # "smallest subnormal"
382 // FB 3E700000001C5F68 # primitive(4499096027744984936)
383 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest subnormal", 0.0000000596046448);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800384
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700385 // The smallest possible half-precision snormal, but digitis are lost converting
386 // to half, so this turns into a single TODO: confirm this is right
387 // 6F # text(15)
388 // 736D616C6C657374206E6F726D616C # "smallest normal"
389 // FA 387FFFFF # primitive(947912703)
390 // in hex single is 0x387fffff, exponent -15, significand 7fffff
391 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest normal", 0.0000610351526F);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800392
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700393 // 71 # text(17)
394 // 62696767657374207375626E6F726D616C # "biggest subnormal"
395 // F9 0400 # primitive(1024)
396 // in hex single is 0x38800000, exponent -14, significand 0
397 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest subnormal", 0.0000610351563F);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800398
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700399 // 70 # text(16)
400 // 7375626E6F726D616C2073696E676C65 # "subnormal single"
401 // FB 37C16C2800000000 # primitive(4017611261645684736)
402 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "subnormal single", 4e-40F);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800403
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700404 // 03 # unsigned(3)
405 // F9 C000 # primitive(49152)
406 QCBOREncode_AddDoubleAsSmallestToMapN(&EC, 3, -2.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800407
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700408 // 70 # text(16)
409 // 6C617267652073696E676C6520657870 # "large single exp"
410 // FA 7F400000 # primitive(2134900736)
411 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((127LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
412 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "large single exp", 2.5521177519070385E+38); // Exponent fits single
413
414 // 74 # text(20)
415 // 746F6F2D6C617267652073696E676C6520657870 # "too-large single exp"
416 // FB 47F8000000000000 # primitive(5185894970917126144)
417 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((128LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
Laurence Lundbladeee851742020-01-08 08:37:05 -0800418 // Exponent too large for single
419 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large single exp", 5.104235503814077E+38);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700420
421 // 66 # text(6)
422 // 646664666465 # "dfdfde"
423 // FA 4B800000 # primitive(1266679808)
Laurence Lundbladeee851742020-01-08 08:37:05 -0800424 // Single with no precision loss
425 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest single with prec", 16777216);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800426
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700427 // 78 18 # text(24)
428 // 626967676573742073696E676C6520776974682070726563 # "biggest single with prec"
429 // FA 4B800000 # primitive(1266679808)
Laurence Lundbladeee851742020-01-08 08:37:05 -0800430 // Double becuase of precision loss
431 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "first single with prec loss", 16777217);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800432
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700433 // Just a convenient marker when cutting and pasting encoded CBOR
434 QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
435
436 QCBOREncode_CloseMap(&EC);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800437
Laurence Lundblade781fd822018-10-01 09:37:52 -0700438 UsefulBufC EncodedHalfs;
Laurence Lundblade0595e932018-11-02 22:22:47 +0700439 int nReturn = QCBOREncode_Finish(&EC, &EncodedHalfs);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700440 if(nReturn) {
441 return -1;
442 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800443
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530444 if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedSmallest))) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700445 return -3;
446 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800447
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700448 return 0;
Laurence Lundblade570fab52018-10-13 18:28:27 +0800449}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700450
451
452
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700453#ifdef NAN_EXPERIMENT
454/*
455 Code for checking what the double to float cast does with
456 NaNs. Not run as part of tests. Keep it around to
457 be able to check various platforms and CPUs.
458 */
459
460#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
461#define DOUBLE_NUM_EXPONENT_BITS (11)
462#define DOUBLE_NUM_SIGN_BITS (1)
463
464#define DOUBLE_SIGNIFICAND_SHIFT (0)
465#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
466#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
467
468#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
469#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
470#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
471#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
472
473
474static int NaNExperiments() {
475 double dqNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT);
476 double dsNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | 0x01);
477 double dqNaNPayload = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT | 0xf00f);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800478
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700479 float f1 = (float)dqNaN;
480 float f2 = (float)dsNaN;
481 float f3 = (float)dqNaNPayload;
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800482
483
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700484 uint32_t uqNaN = UsefulBufUtil_CopyFloatToUint32((float)dqNaN);
485 uint32_t usNaN = UsefulBufUtil_CopyFloatToUint32((float)dsNaN);
486 uint32_t uqNaNPayload = UsefulBufUtil_CopyFloatToUint32((float)dqNaNPayload);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800487
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700488 // Result of this on x86 is that every NaN is a qNaN. The intel
489 // CVTSD2SS instruction ignores the NaN payload and even converts
490 // a sNaN to a qNaN.
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800491
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700492 return 0;
493}
494#endif
495
Laurence Lundblade9682a532020-06-06 18:33:04 -0700496#endif /* QCBOR_CONFIG_DISABLE_ENCODE_IEEE754 */
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700497
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700498