blob: e6facae52bf67b6f803b715f031977b977a6764e [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 Lundblade2d85ce42018-10-12 14:12:47 +080013#include "float_tests.h"
Laurence Lundblade844bb5c2020-03-01 17:27:25 -080014#include "qcbor/qcbor_encode.h"
15#include "qcbor/qcbor_decode.h"
Laurence Lundbladed711fb22018-09-26 14:35:22 -070016#include "half_to_double_from_rfc7049.h"
17#include <math.h> // For INFINITY and NAN and isnan()
Laurence Lundblade68a13352018-09-23 02:19:54 -070018
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080019
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080020
Laurence Lundbladebb474be2018-10-22 11:53:21 +053021static const uint8_t spExpectedHalf[] = {
Laurence Lundblade7d40d812018-09-30 02:44:01 -070022 0xB1,
Laurence Lundblade68a13352018-09-23 02:19:54 -070023 0x64,
24 0x7A, 0x65, 0x72, 0x6F,
25 0xF9, 0x00, 0x00, // 0.000
26 0x6A,
27 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
28 0xF9, 0x7C, 0x00, // Infinity
29 0x73,
Laurence Lundbladeee851742020-01-08 08:37:05 -080030 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6E,
31 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
Laurence Lundblade68a13352018-09-23 02:19:54 -070032 0xF9, 0xFC, 0x00, // -Inifinity
33 0x63,
34 0x4E, 0x61, 0x4E,
35 0xF9, 0x7E, 0x00, // NaN
36 0x63,
37 0x6F, 0x6E, 0x65,
38 0xF9, 0x3C, 0x00, // 1.0
39 0x69,
40 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
41 0xF9, 0x35, 0x55, // 0.333251953125
42 0x76,
Laurence Lundbladeee851742020-01-08 08:37:05 -080043 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C,
44 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
Laurence Lundblade68a13352018-09-23 02:19:54 -070045 0xF9, 0x7B, 0xFF, // 65504.0
Laurence Lundbladeee851742020-01-08 08:37:05 -080046 0x78, 0x18,
47 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x68,
48 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69,
49 0x6F, 0x6E,
Laurence Lundblade68a13352018-09-23 02:19:54 -070050 0xF9, 0x7C, 0x00, // Infinity
51 0x72,
Laurence Lundbladeee851742020-01-08 08:37:05 -080052 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
53 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
Laurence Lundblade68a13352018-09-23 02:19:54 -070054 0xF9, 0x00, 0x01, // 0.000000059604
55 0x6F,
Laurence Lundbladeee851742020-01-08 08:37:05 -080056 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
57 0x72, 0x6D, 0x61, 0x6C,
Laurence Lundblade68a13352018-09-23 02:19:54 -070058 0xF9, 0x03, 0xFF, // 0.0000609755516
59 0x71,
Laurence Lundbladeee851742020-01-08 08:37:05 -080060 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62,
61 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
Laurence Lundblade68a13352018-09-23 02:19:54 -070062 0xF9, 0x04, 0x00, // 0.000061988
63 0x70,
Laurence Lundbladeee851742020-01-08 08:37:05 -080064 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x73,
65 0x69, 0x6E, 0x67, 0x6C, 0x65,
Laurence Lundblade68a13352018-09-23 02:19:54 -070066 0xF9, 0x00, 0x00,
67 0x03,
Laurence Lundblade7d40d812018-09-30 02:44:01 -070068 0xF9, 0xC0, 0x00, // -2
69 0x04,
70 0xF9, 0x7E, 0x00, // qNaN
71 0x05,
72 0xF9, 0x7C, 0x01, // sNaN
73 0x06,
74 0xF9, 0x7E, 0x0F, // qNaN with payload 0x0f
75 0x07,
76 0xF9, 0x7C, 0x0F, // sNaN with payload 0x0f
Laurence Lundblade3aee3a32018-12-17 16:17:45 -080077
Laurence Lundblade68a13352018-09-23 02:19:54 -070078};
79
80
Laurence Lundbladec5fef682020-01-25 11:38:45 -080081int32_t HalfPrecisionDecodeBasicTests()
Laurence Lundblade68a13352018-09-23 02:19:54 -070082{
Laurence Lundbladebb474be2018-10-22 11:53:21 +053083 UsefulBufC HalfPrecision = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedHalf);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -080084
Laurence Lundblade68a13352018-09-23 02:19:54 -070085 QCBORDecodeContext DC;
86 QCBORDecode_Init(&DC, HalfPrecision, 0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -080087
Laurence Lundblade68a13352018-09-23 02:19:54 -070088 QCBORItem Item;
89
90 QCBORDecode_GetNext(&DC, &Item);
91 if(Item.uDataType != QCBOR_TYPE_MAP) {
92 return -1;
93 }
94
95 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +070096 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -070097 return -2;
Laurence Lundblade68a13352018-09-23 02:19:54 -070098 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -080099
Laurence Lundblade68a13352018-09-23 02:19:54 -0700100 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700101 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700102 return -3;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700103 }
104
105 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700106 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700107 return -4;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700108 }
109
Laurence Lundbladeee851742020-01-08 08:37:05 -0800110 // TODO, is this really converting right? It is carrying payload, but
111 // this confuses things.
112 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700113 if(Item.uDataType != QCBOR_TYPE_DOUBLE || !isnan(Item.val.dfnum)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700114 return -5;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700115 }
116
117 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700118 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 1.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700119 return -6;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700120 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800121
Laurence Lundblade68a13352018-09-23 02:19:54 -0700122 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700123 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.333251953125F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700124 return -7;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700125 }
126
127 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700128 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 65504.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700129 return -8;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700130 }
131
132 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700133 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700134 return -9;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700135 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800136
Laurence Lundblade68a13352018-09-23 02:19:54 -0700137 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700138 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000000596046448F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700139 return -10;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700140 }
141
142 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700143 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000609755516F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700144 return -11;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700145 }
146
147 QCBORDecode_GetNext(&DC, &Item); // TODO check this
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700148 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000610351563F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700149 return -12;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700150 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800151
152 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700153 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700154 return -13;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700155 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800156
Laurence Lundblade68a13352018-09-23 02:19:54 -0700157 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700158 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -2.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700159 return -14;
160 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800161
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700162 // TODO: double check these four tests
163 QCBORDecode_GetNext(&DC, &Item); // qNaN
Laurence Lundbladeee851742020-01-08 08:37:05 -0800164 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
165 UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff8000000000000ULL) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700166 return -15;
167 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700168 QCBORDecode_GetNext(&DC, &Item); // sNaN
Laurence Lundbladeee851742020-01-08 08:37:05 -0800169 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
170 UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff0000000000001ULL) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700171 return -16;
172 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700173 QCBORDecode_GetNext(&DC, &Item); // qNaN with payload 0x0f
Laurence Lundbladeee851742020-01-08 08:37:05 -0800174 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
175 UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff800000000000fULL) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700176 return -17;
177 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700178 QCBORDecode_GetNext(&DC, &Item); // sNaN with payload 0x0f
Laurence Lundbladeee851742020-01-08 08:37:05 -0800179 if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
180 UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff000000000000fULL) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700181 return -18;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700182 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700183
Laurence Lundblade68a13352018-09-23 02:19:54 -0700184 if(QCBORDecode_Finish(&DC)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700185 return -19;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700186 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800187
Laurence Lundblade68a13352018-09-23 02:19:54 -0700188 return 0;
189}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700190
191
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700192
193
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800194int32_t HalfPrecisionAgainstRFCCodeTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700195{
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700196 for(uint32_t uHalfP = 0; uHalfP < 0xffff; uHalfP += 60) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700197 unsigned char x[2];
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800198 x[1] = (uint8_t)(uHalfP & 0xff);
199 x[0] = (uint8_t)(uHalfP >> 8); // uHalfP is always less than 0xffff
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700200 double d = decode_half(x);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800201
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700202 // Contruct the CBOR for the half-precision float by hand
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530203 UsefulBuf_MAKE_STACK_UB(__xx, 3);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700204 UsefulOutBuf UOB;
205 UsefulOutBuf_Init(&UOB, __xx);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800206
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800207 const uint8_t uHalfPrecInitialByte = (uint8_t)(HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5)); // 0xf9
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700208 UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
209 UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
210
Laurence Lundbladeee851742020-01-08 08:37:05 -0800211 // Now parse the hand-constructed CBOR. This will invoke the
212 // conversion to a float
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700213 QCBORDecodeContext DC;
214 QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800215
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700216 QCBORItem Item;
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800217
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700218 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700219 if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700220 return -1;
221 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800222
Laurence Lundbladeee851742020-01-08 08:37:05 -0800223 //printf("%04x QCBOR:%15.15f RFC: %15.15f (%8x)\n",
224 // uHalfP, Item.val.fnum, d , UsefulBufUtil_CopyFloatToUint32(d));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800225
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700226 if(isnan(d)) {
227 // The RFC code uses the native instructions which may or may not
228 // handle sNaN, qNaN and NaN payloads correctly. This test just
229 // makes sure it is a NaN and doesn't worry about the type of NaN
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700230 if(!isnan(Item.val.dfnum)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700231 return -3;
232 }
233 } else {
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700234 if(Item.val.dfnum != d) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700235 return -2;
236 }
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700237 }
238 }
239 return 0;
240}
241
242
243/*
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530244 {"zero": 0.0,
245 "negative zero": -0.0,
246 "infinitity": Infinity,
247 "negative infinitity": -Infinity,
248 "NaN": NaN,
249 "one": 1.0,
250 "one third": 0.333251953125,
251 "largest half-precision": 65504.0,
252 "largest half-precision point one": 65504.1,
253 "too-large half-precision": 65536.0,
254 "smallest subnormal": 5.96046448e-8,
255 "smallest normal": 0.00006103515261202119,
256 "biggest subnormal": 0.00006103515625,
257 "subnormal single": 4.00000646641519e-40,
258 3: -2.0,
259 "large single exp": 2.5521177519070385e+38,
260 "too-large single exp": 5.104235503814077e+38,
261 "biggest single with prec": 16777216.0,
262 "first single with prec loss": 16777217.0,
263 1: "fin"}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700264 */
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530265static const uint8_t spExpectedSmallest[] = {
266 0xB4, 0x64, 0x7A, 0x65, 0x72, 0x6F, 0xF9, 0x00, 0x00, 0x6D,
267 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x7A,
268 0x65, 0x72, 0x6F, 0xF9, 0x80, 0x00, 0x6A, 0x69, 0x6E, 0x66,
269 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79, 0xF9, 0x7C, 0x00,
270 0x73, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20,
271 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
272 0xF9, 0xFC, 0x00, 0x63, 0x4E, 0x61, 0x4E, 0xF9, 0x7E, 0x00,
273 0x63, 0x6F, 0x6E, 0x65, 0xF9, 0x3C, 0x00, 0x69, 0x6F, 0x6E,
274 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0xF9, 0x35, 0x55,
275 0x76, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68,
276 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73,
277 0x69, 0x6F, 0x6E, 0xF9, 0x7B, 0xFF, 0x78, 0x20, 0x6C, 0x61,
278 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66,
279 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
280 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, 0x6F, 0x6E, 0x65,
281 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33, 0x78,
282 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65,
283 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63,
284 0x69, 0x73, 0x69, 0x6F, 0x6E, 0xFA, 0x47, 0x80, 0x00, 0x00,
285 0x72, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
286 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xFB,
287 0x3E, 0x70, 0x00, 0x00, 0x00, 0x1C, 0x5F, 0x68, 0x6F, 0x73,
288 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
289 0x72, 0x6D, 0x61, 0x6C, 0xFA, 0x38, 0x7F, 0xFF, 0xFF, 0x71,
290 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
291 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xF9, 0x04, 0x00,
292 0x70, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
293 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0xFB, 0x37, 0xC1,
294 0x6C, 0x28, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF9, 0xC0, 0x00,
295 0x70, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E,
296 0x67, 0x6C, 0x65, 0x20, 0x65, 0x78, 0x70, 0xFA, 0x7F, 0x40,
297 0x00, 0x00, 0x74, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72,
298 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20,
299 0x65, 0x78, 0x70, 0xFB, 0x47, 0xF8, 0x00, 0x00, 0x00, 0x00,
300 0x00, 0x00, 0x78, 0x18, 0x62, 0x69, 0x67, 0x67, 0x65, 0x73,
301 0x74, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77,
302 0x69, 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0xFA, 0x4B,
303 0x80, 0x00, 0x00, 0x78, 0x1B, 0x66, 0x69, 0x72, 0x73, 0x74,
304 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77, 0x69,
305 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x20, 0x6C, 0x6F,
306 0x73, 0x73, 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00,
307 0x00, 0x01, 0x63, 0x66, 0x69, 0x6E
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700308};
309
310
Laurence Lundbladec5fef682020-01-25 11:38:45 -0800311int32_t DoubleAsSmallestTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700312{
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530313 UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, 420);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800314
Laurence Lundblade067035b2018-11-28 17:35:25 -0800315#define QCBOREncode_AddDoubleAsSmallestToMap QCBOREncode_AddDoubleToMap
316#define QCBOREncode_AddDoubleAsSmallestToMapN QCBOREncode_AddDoubleToMapN
317
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800318
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700319 QCBOREncodeContext EC;
320 QCBOREncode_Init(&EC, EncodedHalfsMem);
321 // These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
322 QCBOREncode_OpenMap(&EC);
323 // 64 # text(4)
324 // 7A65726F # "zero"
325 // F9 0000 # primitive(0)
326 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "zero", 0.00);
327
328 // 64 # text(4)
329 // 7A65726F # "negative zero"
330 // F9 8000 # primitive(0)
331 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative zero", -0.00);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800332
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700333 // 6A # text(10)
334 // 696E66696E6974697479 # "infinitity"
335 // F9 7C00 # primitive(31744)
336 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "infinitity", INFINITY);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800337
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700338 // 73 # text(19)
339 // 6E6567617469766520696E66696E6974697479 # "negative infinitity"
340 // F9 FC00 # primitive(64512)
341 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative infinitity", -INFINITY);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800342
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700343 // 63 # text(3)
344 // 4E614E # "NaN"
345 // F9 7E00 # primitive(32256)
346 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "NaN", NAN);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800347
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700348 // TODO: test a few NaN variants
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800349
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700350 // 63 # text(3)
351 // 6F6E65 # "one"
352 // F9 3C00 # primitive(15360)
353 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one", 1.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800354
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700355 // 69 # text(9)
356 // 6F6E65207468697264 # "one third"
357 // F9 3555 # primitive(13653)
358 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one third", 0.333251953125);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800359
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700360 // 76 # text(22)
Laurence Lundbladeee851742020-01-08 08:37:05 -0800361 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700362 // F9 7BFF # primitive(31743)
363 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision",65504.0);
364
365 // 76 # text(22)
Laurence Lundbladeee851742020-01-08 08:37:05 -0800366 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700367 // F9 7BFF # primitive(31743)
368 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision point one",65504.1);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800369
Laurence Lundbladeee851742020-01-08 08:37:05 -0800370 // Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which
371 // is larger than 15, the largest half-precision exponent
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700372 // 78 18 # text(24)
373 // 746F6F2D6C617267652068616C662D707265636973696F6E # "too-large half-precision"
374 // FA 47800000 # primitive(31743)
375 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large half-precision", 65536.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800376
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700377 // The smallest possible half-precision subnormal, but digitis are lost converting
378 // to half, so this turns into a double
379 // 72 # text(18)
380 // 736D616C6C657374207375626E6F726D616C # "smallest subnormal"
381 // FB 3E700000001C5F68 # primitive(4499096027744984936)
382 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest subnormal", 0.0000000596046448);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800383
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700384 // The smallest possible half-precision snormal, but digitis are lost converting
385 // to half, so this turns into a single TODO: confirm this is right
386 // 6F # text(15)
387 // 736D616C6C657374206E6F726D616C # "smallest normal"
388 // FA 387FFFFF # primitive(947912703)
389 // in hex single is 0x387fffff, exponent -15, significand 7fffff
390 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest normal", 0.0000610351526F);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800391
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700392 // 71 # text(17)
393 // 62696767657374207375626E6F726D616C # "biggest subnormal"
394 // F9 0400 # primitive(1024)
395 // in hex single is 0x38800000, exponent -14, significand 0
396 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest subnormal", 0.0000610351563F);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800397
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700398 // 70 # text(16)
399 // 7375626E6F726D616C2073696E676C65 # "subnormal single"
400 // FB 37C16C2800000000 # primitive(4017611261645684736)
401 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "subnormal single", 4e-40F);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800402
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700403 // 03 # unsigned(3)
404 // F9 C000 # primitive(49152)
405 QCBOREncode_AddDoubleAsSmallestToMapN(&EC, 3, -2.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800406
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700407 // 70 # text(16)
408 // 6C617267652073696E676C6520657870 # "large single exp"
409 // FA 7F400000 # primitive(2134900736)
410 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((127LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
411 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "large single exp", 2.5521177519070385E+38); // Exponent fits single
412
413 // 74 # text(20)
414 // 746F6F2D6C617267652073696E676C6520657870 # "too-large single exp"
415 // FB 47F8000000000000 # primitive(5185894970917126144)
416 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((128LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
Laurence Lundbladeee851742020-01-08 08:37:05 -0800417 // Exponent too large for single
418 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large single exp", 5.104235503814077E+38);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700419
420 // 66 # text(6)
421 // 646664666465 # "dfdfde"
422 // FA 4B800000 # primitive(1266679808)
Laurence Lundbladeee851742020-01-08 08:37:05 -0800423 // Single with no precision loss
424 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest single with prec", 16777216);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800425
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700426 // 78 18 # text(24)
427 // 626967676573742073696E676C6520776974682070726563 # "biggest single with prec"
428 // FA 4B800000 # primitive(1266679808)
Laurence Lundbladeee851742020-01-08 08:37:05 -0800429 // Double becuase of precision loss
430 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "first single with prec loss", 16777217);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800431
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700432 // Just a convenient marker when cutting and pasting encoded CBOR
433 QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
434
435 QCBOREncode_CloseMap(&EC);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800436
Laurence Lundblade781fd822018-10-01 09:37:52 -0700437 UsefulBufC EncodedHalfs;
Laurence Lundblade0595e932018-11-02 22:22:47 +0700438 int nReturn = QCBOREncode_Finish(&EC, &EncodedHalfs);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700439 if(nReturn) {
440 return -1;
441 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800442
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530443 if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedSmallest))) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700444 return -3;
445 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800446
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700447 return 0;
Laurence Lundblade570fab52018-10-13 18:28:27 +0800448}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700449
450
451
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700452#ifdef NAN_EXPERIMENT
453/*
454 Code for checking what the double to float cast does with
455 NaNs. Not run as part of tests. Keep it around to
456 be able to check various platforms and CPUs.
457 */
458
459#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
460#define DOUBLE_NUM_EXPONENT_BITS (11)
461#define DOUBLE_NUM_SIGN_BITS (1)
462
463#define DOUBLE_SIGNIFICAND_SHIFT (0)
464#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
465#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
466
467#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
468#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
469#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
470#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
471
472
473static int NaNExperiments() {
474 double dqNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT);
475 double dsNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | 0x01);
476 double dqNaNPayload = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT | 0xf00f);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800477
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700478 float f1 = (float)dqNaN;
479 float f2 = (float)dsNaN;
480 float f3 = (float)dqNaNPayload;
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800481
482
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700483 uint32_t uqNaN = UsefulBufUtil_CopyFloatToUint32((float)dqNaN);
484 uint32_t usNaN = UsefulBufUtil_CopyFloatToUint32((float)dsNaN);
485 uint32_t uqNaNPayload = UsefulBufUtil_CopyFloatToUint32((float)dqNaNPayload);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800486
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700487 // Result of this on x86 is that every NaN is a qNaN. The intel
488 // CVTSD2SS instruction ignores the NaN payload and even converts
489 // a sNaN to a qNaN.
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800490
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700491 return 0;
492}
493#endif
494
495
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700496