blob: 91bcebdeafc52227be87190b306e064abe5c7b5d [file] [log] [blame]
Laurence Lundblade68a13352018-09-23 02:19:54 -07001/*==============================================================================
2 Copyright 2018 Laurence Lundblade
3
4 Permission is hereby granted, free of charge, to any person obtaining
5 a copy of this software and associated documentation files (the
6 "Software"), to deal in the Software without restriction, including
7 without limitation the rights to use, copy, modify, merge, publish,
8 distribute, sublicense, and/or sell copies of the Software, and to
9 permit persons to whom the Software is furnished to do so, subject to
10 the following conditions:
11
12 The above copyright notice and this permission notice shall be included
13 in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23
24 (This is the MIT license)
25 ==============================================================================*/
26//
27// half_precision_test.c
28// QCBOR
29//
30// Created by Laurence Lundblade on 9/19/18.
31// Copyright © 2018 Laurence Lundblade. All rights reserved.
32//
33
34#include "half_precision_test.h"
35#include "qcbor.h"
36
37#include <math.h> // For INFINITY and NaN
38
39static const uint8_t ExpectedHalf[] = {
40 0xAD,
41 0x64,
42 0x7A, 0x65, 0x72, 0x6F,
43 0xF9, 0x00, 0x00, // 0.000
44 0x6A,
45 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
46 0xF9, 0x7C, 0x00, // Infinity
47 0x73,
48 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
49 0xF9, 0xFC, 0x00, // -Inifinity
50 0x63,
51 0x4E, 0x61, 0x4E,
52 0xF9, 0x7E, 0x00, // NaN
53 0x63,
54 0x6F, 0x6E, 0x65,
55 0xF9, 0x3C, 0x00, // 1.0
56 0x69,
57 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
58 0xF9, 0x35, 0x55, // 0.333251953125
59 0x76,
60 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
61 0xF9, 0x7B, 0xFF, // 65504.0
62 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,
63 0xF9, 0x7C, 0x00, // Infinity
64 0x72,
65 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
66 0xF9, 0x00, 0x01, // 0.000000059604
67 0x6F,
68 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
69 0xF9, 0x03, 0xFF, // 0.0000609755516
70 0x71,
71 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
72 0xF9, 0x04, 0x00, // 0.000061988
73 0x70,
74 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65,
75 0xF9, 0x00, 0x00,
76 0x03,
77 0xF9, 0xC0, 0x00 // -2.0
78};
79
80
81
82int half_precision_encode_basic()
83{
84 UsefulBuf_MakeStackUB(EncodedHalfsMem, 220);
85
86 QCBOREncodeContext EC;
87 QCBOREncode_Init(&EC, EncodedHalfsMem);
88 // These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
89 QCBOREncode_OpenMap(&EC);
90 QCBOREncode_AddFloatAsHalfToMap(&EC, "zero", 0.00F);
91 QCBOREncode_AddFloatAsHalfToMap(&EC, "infinitity", INFINITY);
92 QCBOREncode_AddFloatAsHalfToMap(&EC, "negative infinitity", -INFINITY);
93 QCBOREncode_AddFloatAsHalfToMap(&EC, "NaN", NAN);
94 QCBOREncode_AddFloatAsHalfToMap(&EC, "one", 1.0F);
95 QCBOREncode_AddFloatAsHalfToMap(&EC, "one third", 0.333251953125F);
96 QCBOREncode_AddFloatAsHalfToMap(&EC, "largest half-precision",65504.0F);
97 // Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which is larger than 15, the largest half-precision exponent
98 QCBOREncode_AddFloatAsHalfToMap(&EC, "too-large half-precision", 65536.0F);
99 // Should convert to smallest possible half precision which is encodded as 0x00 0x01 or 5.960464477539063e-8
100 QCBOREncode_AddFloatAsHalfToMap(&EC, "smallest subnormal", 0.0000000596046448);
101 QCBOREncode_AddFloatAsHalfToMap(&EC, "smallest normal", 0.0000610351526F); // in hex single is 0x387fffff, exponent -15, significand 7fffff
102 QCBOREncode_AddFloatAsHalfToMap(&EC, "biggest subnormal", 0.0000610351563F); // in hex single is 0x38800000, exponent -14, significand 0
103 QCBOREncode_AddFloatAsHalfToMap(&EC, "subnormal single", 4e-40F);
104 QCBOREncode_AddFloatAsHalfToMapN(&EC, 3, -2.0F);
105 QCBOREncode_CloseMap(&EC);
106
107 EncodedCBOR EncodedHalfs;
108
109 int nReturn = QCBOREncode_Finish2(&EC, &EncodedHalfs);
110
111 if(nReturn) {
112 return -1;
113 }
114
115 if(UsefulBuf_Compare(EncodedHalfs.Bytes, UsefulBuf_FromByteArrayLiteral(ExpectedHalf))) {
116 return -3;
117 }
118
119 return 0;
120}
121
122
123int half_precision_decode_basic()
124{
125 UsefulBufC HalfPrecision = UsefulBuf_FromByteArrayLiteral(ExpectedHalf);
126
127 QCBORDecodeContext DC;
128 QCBORDecode_Init(&DC, HalfPrecision, 0);
129
130 QCBORItem Item;
131
132 QCBORDecode_GetNext(&DC, &Item);
133 if(Item.uDataType != QCBOR_TYPE_MAP) {
134 return -1;
135 }
136
137 QCBORDecode_GetNext(&DC, &Item);
138 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0F) {
139 return -1;
140 }
141
142 QCBORDecode_GetNext(&DC, &Item);
143 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != INFINITY) {
144 return -1;
145 }
146
147 QCBORDecode_GetNext(&DC, &Item);
148 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != -INFINITY) {
149 return -1;
150 }
151
152 QCBORDecode_GetNext(&DC, &Item); // TODO, is this really converting right? It is carrying payload, but this confuses things.
153 if(Item.uDataType != QCBOR_TYPE_FLOAT || !isnan(Item.val.fnum)) {
154 return -1;
155 }
156
157 QCBORDecode_GetNext(&DC, &Item);
158 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 1.0F) {
159 return -1;
160 }
161
162 QCBORDecode_GetNext(&DC, &Item);
163 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.333251953125F) {
164 return -1;
165 }
166
167 QCBORDecode_GetNext(&DC, &Item);
168 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 65504.0F) {
169 return -1;
170 }
171
172 QCBORDecode_GetNext(&DC, &Item);
173 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != INFINITY) {
174 return -1;
175 }
176
177 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
178 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0000000596046448F) {
179 return -1;
180 }
181
182 QCBORDecode_GetNext(&DC, &Item); // TODO: check this
183 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0000609755516F) {
184 return -1;
185 }
186
187 QCBORDecode_GetNext(&DC, &Item); // TODO check this
188 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0000610351563F) {
189 return -1;
190 }
191
192 QCBORDecode_GetNext(&DC, &Item);
193 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0) {
194 return -1;
195 }
196
197 QCBORDecode_GetNext(&DC, &Item);
198 if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != -2.0F) {
199 return -1;
200 }
201
202 if(QCBORDecode_Finish(&DC)) {
203 return -1;
204 }
205
206 return 0;
207}