blob: 7bc23aa469a966c616ecb622e090fa8af36b3cbe [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 Lundbladed92a6162018-11-01 11:38:35 +07004 Copyright (c) 2018, Laurence Lundblade.
5 All rights reserved.
Laurence Lundblade3aee3a32018-12-17 16:17:45 -08006
Laurence Lundblade0dbc9172018-11-01 14:17:21 +07007Redistribution and use in source and binary forms, with or without
8modification, are permitted provided that the following conditions are
9met:
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above
13 copyright notice, this list of conditions and the following
14 disclaimer in the documentation and/or other materials provided
15 with the distribution.
16 * The name "Laurence Lundblade" may not be used to
17 endorse or promote products derived from this software without
18 specific prior written permission.
Laurence Lundblade3aee3a32018-12-17 16:17:45 -080019
Laurence Lundblade0dbc9172018-11-01 14:17:21 +070020THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
21WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
23ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
24BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
29OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
30IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Laurence Lundblade68a13352018-09-23 02:19:54 -070031 ==============================================================================*/
Laurence Lundblade68a13352018-09-23 02:19:54 -070032// Created by Laurence Lundblade on 9/19/18.
Laurence Lundblade781fd822018-10-01 09:37:52 -070033
Laurence Lundblade68a13352018-09-23 02:19:54 -070034
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080035#include "float_tests.h"
Laurence Lundblade68a13352018-09-23 02:19:54 -070036#include "qcbor.h"
Laurence Lundbladed711fb22018-09-26 14:35:22 -070037#include "half_to_double_from_rfc7049.h"
38#include <math.h> // For INFINITY and NAN and isnan()
Laurence Lundblade68a13352018-09-23 02:19:54 -070039
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080040
Laurence Lundblade2d85ce42018-10-12 14:12:47 +080041
Laurence Lundbladebb474be2018-10-22 11:53:21 +053042static const uint8_t spExpectedHalf[] = {
Laurence Lundblade7d40d812018-09-30 02:44:01 -070043 0xB1,
Laurence Lundblade68a13352018-09-23 02:19:54 -070044 0x64,
45 0x7A, 0x65, 0x72, 0x6F,
46 0xF9, 0x00, 0x00, // 0.000
47 0x6A,
48 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
49 0xF9, 0x7C, 0x00, // Infinity
50 0x73,
51 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
52 0xF9, 0xFC, 0x00, // -Inifinity
53 0x63,
54 0x4E, 0x61, 0x4E,
55 0xF9, 0x7E, 0x00, // NaN
56 0x63,
57 0x6F, 0x6E, 0x65,
58 0xF9, 0x3C, 0x00, // 1.0
59 0x69,
60 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
61 0xF9, 0x35, 0x55, // 0.333251953125
62 0x76,
63 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
64 0xF9, 0x7B, 0xFF, // 65504.0
65 0x78, 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
66 0xF9, 0x7C, 0x00, // Infinity
67 0x72,
68 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
69 0xF9, 0x00, 0x01, // 0.000000059604
70 0x6F,
71 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
72 0xF9, 0x03, 0xFF, // 0.0000609755516
73 0x71,
74 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
75 0xF9, 0x04, 0x00, // 0.000061988
76 0x70,
77 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65,
78 0xF9, 0x00, 0x00,
79 0x03,
Laurence Lundblade7d40d812018-09-30 02:44:01 -070080 0xF9, 0xC0, 0x00, // -2
81 0x04,
82 0xF9, 0x7E, 0x00, // qNaN
83 0x05,
84 0xF9, 0x7C, 0x01, // sNaN
85 0x06,
86 0xF9, 0x7E, 0x0F, // qNaN with payload 0x0f
87 0x07,
88 0xF9, 0x7C, 0x0F, // sNaN with payload 0x0f
Laurence Lundblade3aee3a32018-12-17 16:17:45 -080089
Laurence Lundblade68a13352018-09-23 02:19:54 -070090};
91
92
Laurence Lundbladebb474be2018-10-22 11:53:21 +053093int HalfPrecisionDecodeBasicTests()
Laurence Lundblade68a13352018-09-23 02:19:54 -070094{
Laurence Lundbladebb474be2018-10-22 11:53:21 +053095 UsefulBufC HalfPrecision = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedHalf);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -080096
Laurence Lundblade68a13352018-09-23 02:19:54 -070097 QCBORDecodeContext DC;
98 QCBORDecode_Init(&DC, HalfPrecision, 0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -080099
Laurence Lundblade68a13352018-09-23 02:19:54 -0700100 QCBORItem Item;
101
102 QCBORDecode_GetNext(&DC, &Item);
103 if(Item.uDataType != QCBOR_TYPE_MAP) {
104 return -1;
105 }
106
107 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700108 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700109 return -2;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700110 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800111
Laurence Lundblade68a13352018-09-23 02:19:54 -0700112 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700113 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700114 return -3;
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 != -INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700119 return -4;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700120 }
121
122 QCBORDecode_GetNext(&DC, &Item); // TODO, is this really converting right? It is carrying payload, but this confuses things.
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700123 if(Item.uDataType != QCBOR_TYPE_DOUBLE || !isnan(Item.val.dfnum)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700124 return -5;
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 != 1.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700129 return -6;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700130 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800131
Laurence Lundblade68a13352018-09-23 02:19:54 -0700132 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700133 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.333251953125F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700134 return -7;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700135 }
136
137 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700138 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 65504.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700139 return -8;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700140 }
141
142 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700143 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700144 return -9;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700145 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800146
Laurence Lundblade68a13352018-09-23 02:19:54 -0700147 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.0000000596046448F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700149 return -10;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700150 }
151
152 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700153 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000609755516F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700154 return -11;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700155 }
156
157 QCBORDecode_GetNext(&DC, &Item); // TODO check this
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700158 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000610351563F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700159 return -12;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700160 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800161
162 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700163 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700164 return -13;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700165 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800166
Laurence Lundblade68a13352018-09-23 02:19:54 -0700167 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700168 if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -2.0F) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700169 return -14;
170 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800171
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700172 // TODO: double check these four tests
173 QCBORDecode_GetNext(&DC, &Item); // qNaN
174 if(Item.uDataType != QCBOR_TYPE_DOUBLE || UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff8000000000000ULL) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700175 return -15;
176 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700177 QCBORDecode_GetNext(&DC, &Item); // sNaN
178 if(Item.uDataType != QCBOR_TYPE_DOUBLE || UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff0000000000001ULL) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700179 return -16;
180 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700181 QCBORDecode_GetNext(&DC, &Item); // qNaN with payload 0x0f
182 if(Item.uDataType != QCBOR_TYPE_DOUBLE || UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff800000000000fULL) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700183 return -17;
184 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700185 QCBORDecode_GetNext(&DC, &Item); // sNaN with payload 0x0f
186 if(Item.uDataType != QCBOR_TYPE_DOUBLE || UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff000000000000fULL) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700187 return -18;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700188 }
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700189
Laurence Lundblade68a13352018-09-23 02:19:54 -0700190 if(QCBORDecode_Finish(&DC)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700191 return -19;
Laurence Lundblade68a13352018-09-23 02:19:54 -0700192 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800193
Laurence Lundblade68a13352018-09-23 02:19:54 -0700194 return 0;
195}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700196
197
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700198
199
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530200int HalfPrecisionAgainstRFCCodeTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700201{
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700202 for(uint32_t uHalfP = 0; uHalfP < 0xffff; uHalfP += 60) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700203 unsigned char x[2];
204 x[1] = uHalfP & 0xff;
205 x[0] = uHalfP >> 8;
206 double d = decode_half(x);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800207
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700208 // Contruct the CBOR for the half-precision float by hand
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530209 UsefulBuf_MAKE_STACK_UB(__xx, 3);
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700210 UsefulOutBuf UOB;
211 UsefulOutBuf_Init(&UOB, __xx);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800212
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700213 const uint8_t uHalfPrecInitialByte = HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5); // 0xf9
214 UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
215 UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
216
217 // Now parse the hand-constructed CBOR. This will invoke the conversion to a float
218 QCBORDecodeContext DC;
219 QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800220
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700221 QCBORItem Item;
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800222
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700223 QCBORDecode_GetNext(&DC, &Item);
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700224 if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700225 return -1;
226 }
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800227
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700228 //printf("%04x QCBOR:%15.15f RFC: %15.15f (%8x)\n", uHalfP,Item.val.fnum, d , UsefulBufUtil_CopyFloatToUint32(d));
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800229
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700230 if(isnan(d)) {
231 // The RFC code uses the native instructions which may or may not
232 // handle sNaN, qNaN and NaN payloads correctly. This test just
233 // makes sure it is a NaN and doesn't worry about the type of NaN
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700234 if(!isnan(Item.val.dfnum)) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700235 return -3;
236 }
237 } else {
Laurence Lundblade67bd5512018-11-02 21:44:06 +0700238 if(Item.val.dfnum != d) {
Laurence Lundblade7d40d812018-09-30 02:44:01 -0700239 return -2;
240 }
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700241 }
242 }
243 return 0;
244}
245
246
247/*
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530248 {"zero": 0.0,
249 "negative zero": -0.0,
250 "infinitity": Infinity,
251 "negative infinitity": -Infinity,
252 "NaN": NaN,
253 "one": 1.0,
254 "one third": 0.333251953125,
255 "largest half-precision": 65504.0,
256 "largest half-precision point one": 65504.1,
257 "too-large half-precision": 65536.0,
258 "smallest subnormal": 5.96046448e-8,
259 "smallest normal": 0.00006103515261202119,
260 "biggest subnormal": 0.00006103515625,
261 "subnormal single": 4.00000646641519e-40,
262 3: -2.0,
263 "large single exp": 2.5521177519070385e+38,
264 "too-large single exp": 5.104235503814077e+38,
265 "biggest single with prec": 16777216.0,
266 "first single with prec loss": 16777217.0,
267 1: "fin"}
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700268 */
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530269static const uint8_t spExpectedSmallest[] = {
270 0xB4, 0x64, 0x7A, 0x65, 0x72, 0x6F, 0xF9, 0x00, 0x00, 0x6D,
271 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x7A,
272 0x65, 0x72, 0x6F, 0xF9, 0x80, 0x00, 0x6A, 0x69, 0x6E, 0x66,
273 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79, 0xF9, 0x7C, 0x00,
274 0x73, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20,
275 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
276 0xF9, 0xFC, 0x00, 0x63, 0x4E, 0x61, 0x4E, 0xF9, 0x7E, 0x00,
277 0x63, 0x6F, 0x6E, 0x65, 0xF9, 0x3C, 0x00, 0x69, 0x6F, 0x6E,
278 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0xF9, 0x35, 0x55,
279 0x76, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68,
280 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73,
281 0x69, 0x6F, 0x6E, 0xF9, 0x7B, 0xFF, 0x78, 0x20, 0x6C, 0x61,
282 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66,
283 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
284 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, 0x6F, 0x6E, 0x65,
285 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33, 0x78,
286 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65,
287 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63,
288 0x69, 0x73, 0x69, 0x6F, 0x6E, 0xFA, 0x47, 0x80, 0x00, 0x00,
289 0x72, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
290 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xFB,
291 0x3E, 0x70, 0x00, 0x00, 0x00, 0x1C, 0x5F, 0x68, 0x6F, 0x73,
292 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
293 0x72, 0x6D, 0x61, 0x6C, 0xFA, 0x38, 0x7F, 0xFF, 0xFF, 0x71,
294 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
295 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xF9, 0x04, 0x00,
296 0x70, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
297 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0xFB, 0x37, 0xC1,
298 0x6C, 0x28, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF9, 0xC0, 0x00,
299 0x70, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E,
300 0x67, 0x6C, 0x65, 0x20, 0x65, 0x78, 0x70, 0xFA, 0x7F, 0x40,
301 0x00, 0x00, 0x74, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72,
302 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20,
303 0x65, 0x78, 0x70, 0xFB, 0x47, 0xF8, 0x00, 0x00, 0x00, 0x00,
304 0x00, 0x00, 0x78, 0x18, 0x62, 0x69, 0x67, 0x67, 0x65, 0x73,
305 0x74, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77,
306 0x69, 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0xFA, 0x4B,
307 0x80, 0x00, 0x00, 0x78, 0x1B, 0x66, 0x69, 0x72, 0x73, 0x74,
308 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77, 0x69,
309 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x20, 0x6C, 0x6F,
310 0x73, 0x73, 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00,
311 0x00, 0x01, 0x63, 0x66, 0x69, 0x6E
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700312};
313
314
Laurence Lundbladebb474be2018-10-22 11:53:21 +0530315int DoubleAsSmallestTest()
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700316{
Laurence Lundblade4fe9f312018-10-22 10:22:39 +0530317 UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, 420);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800318
Laurence Lundblade067035b2018-11-28 17:35:25 -0800319#define QCBOREncode_AddDoubleAsSmallestToMap QCBOREncode_AddDoubleToMap
320#define QCBOREncode_AddDoubleAsSmallestToMapN QCBOREncode_AddDoubleToMapN
321
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800322
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700323 QCBOREncodeContext EC;
324 QCBOREncode_Init(&EC, EncodedHalfsMem);
325 // These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
326 QCBOREncode_OpenMap(&EC);
327 // 64 # text(4)
328 // 7A65726F # "zero"
329 // F9 0000 # primitive(0)
330 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "zero", 0.00);
331
332 // 64 # text(4)
333 // 7A65726F # "negative zero"
334 // F9 8000 # primitive(0)
335 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative zero", -0.00);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800336
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700337 // 6A # text(10)
338 // 696E66696E6974697479 # "infinitity"
339 // F9 7C00 # primitive(31744)
340 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "infinitity", INFINITY);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800341
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700342 // 73 # text(19)
343 // 6E6567617469766520696E66696E6974697479 # "negative infinitity"
344 // F9 FC00 # primitive(64512)
345 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative infinitity", -INFINITY);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800346
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700347 // 63 # text(3)
348 // 4E614E # "NaN"
349 // F9 7E00 # primitive(32256)
350 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "NaN", NAN);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800351
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700352 // TODO: test a few NaN variants
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800353
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700354 // 63 # text(3)
355 // 6F6E65 # "one"
356 // F9 3C00 # primitive(15360)
357 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one", 1.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800358
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700359 // 69 # text(9)
360 // 6F6E65207468697264 # "one third"
361 // F9 3555 # primitive(13653)
362 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one third", 0.333251953125);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800363
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700364 // 76 # text(22)
365 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
366 // F9 7BFF # primitive(31743)
367 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision",65504.0);
368
369 // 76 # text(22)
370 // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
371 // F9 7BFF # primitive(31743)
372 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision point one",65504.1);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800373
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700374 // Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which is larger than 15, the largest half-precision exponent
375 // 78 18 # text(24)
376 // 746F6F2D6C617267652068616C662D707265636973696F6E # "too-large half-precision"
377 // FA 47800000 # primitive(31743)
378 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large half-precision", 65536.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800379
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700380 // The smallest possible half-precision subnormal, but digitis are lost converting
381 // to half, so this turns into a double
382 // 72 # text(18)
383 // 736D616C6C657374207375626E6F726D616C # "smallest subnormal"
384 // FB 3E700000001C5F68 # primitive(4499096027744984936)
385 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest subnormal", 0.0000000596046448);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800386
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700387 // The smallest possible half-precision snormal, but digitis are lost converting
388 // to half, so this turns into a single TODO: confirm this is right
389 // 6F # text(15)
390 // 736D616C6C657374206E6F726D616C # "smallest normal"
391 // FA 387FFFFF # primitive(947912703)
392 // in hex single is 0x387fffff, exponent -15, significand 7fffff
393 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest normal", 0.0000610351526F);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800394
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700395 // 71 # text(17)
396 // 62696767657374207375626E6F726D616C # "biggest subnormal"
397 // F9 0400 # primitive(1024)
398 // in hex single is 0x38800000, exponent -14, significand 0
399 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest subnormal", 0.0000610351563F);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800400
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700401 // 70 # text(16)
402 // 7375626E6F726D616C2073696E676C65 # "subnormal single"
403 // FB 37C16C2800000000 # primitive(4017611261645684736)
404 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "subnormal single", 4e-40F);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800405
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700406 // 03 # unsigned(3)
407 // F9 C000 # primitive(49152)
408 QCBOREncode_AddDoubleAsSmallestToMapN(&EC, 3, -2.0);
Laurence Lundblade3aee3a32018-12-17 16:17:45 -0800409
Laurence Lundbladed711fb22018-09-26 14:35:22 -0700410 // 70 # text(16)
411 // 6C617267652073696E676C6520657870 # "large single exp"
412 // FA 7F400000 # primitive(2134900736)
413 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((127LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
414 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "large single exp", 2.5521177519070385E+38); // Exponent fits single
415
416 // 74 # text(20)
417 // 746F6F2D6C617267652073696E676C6520657870 # "too-large single exp"
418 // FB 47F8000000000000 # primitive(5185894970917126144)
419 // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((128LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
420 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large single exp", 5.104235503814077E+38); // Exponent too large for single
421
422 // 66 # text(6)
423 // 646664666465 # "dfdfde"
424 // FA 4B800000 # primitive(1266679808)
425 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest single with prec",16777216); // Single with no precision loss
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)
430 QCBOREncode_AddDoubleAsSmallestToMap(&EC, "first single with prec loss",16777217); // Double becuase of precision loss
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