Merge floating-point disabling into spiffy decode
diff --git a/test/float_tests.c b/test/float_tests.c
index e7696d1..5aefaea 100644
--- a/test/float_tests.c
+++ b/test/float_tests.c
@@ -10,56 +10,81 @@
Created on 9/19/18
=============================================================================*/
+
#include "float_tests.h"
#include "qcbor/qcbor_encode.h"
#include "qcbor/qcbor_decode.h"
-#include "half_to_double_from_rfc7049.h"
#include <math.h> // For INFINITY and NAN and isnan()
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+
+#include "half_to_double_from_rfc7049.h"
+/*
+ Half-precision values that are input to test half-precision decoding
+
+ As decoded by http://cbor.me
+ {"zero": 0.0,
+ "infinitity": Infinity,
+ "negative infinitity": -Infinity,
+ "NaN": NaN,
+ "one": 1.0,
+ "one third": 0.333251953125,
+ "largest half-precision": 65504.0,
+ "too-large half-precision": Infinity,
+ "smallest subnormal": 5.960464477539063e-8,
+ "smallest normal": 0.00006097555160522461,
+ "biggest subnormal": 0.00006103515625,
+ "subnormal single": 0.0,
+ 3: -2.0,
+ 4: NaN,
+ 5: NaN,
+ 6: NaN,
+ 7: NaN}
+ */
static const uint8_t spExpectedHalf[] = {
0xB1,
0x64,
0x7A, 0x65, 0x72, 0x6F,
- 0xF9, 0x00, 0x00, // 0.000
+ 0xF9, 0x00, 0x00, // half-precision 0.000
0x6A,
0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
- 0xF9, 0x7C, 0x00, // Infinity
+ 0xF9, 0x7C, 0x00, // Infinity
0x73,
0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6E,
0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
- 0xF9, 0xFC, 0x00, // -Inifinity
+ 0xF9, 0xFC, 0x00, // -Inifinity
0x63,
0x4E, 0x61, 0x4E,
- 0xF9, 0x7E, 0x00, // NaN
+ 0xF9, 0x7E, 0x00, // NaN
0x63,
0x6F, 0x6E, 0x65,
- 0xF9, 0x3C, 0x00, // 1.0
+ 0xF9, 0x3C, 0x00, // 1.0
0x69,
0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
- 0xF9, 0x35, 0x55, // 0.333251953125
+ 0xF9, 0x35, 0x55, // half-precsion one third 0.333251953125
0x76,
0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C,
0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
- 0xF9, 0x7B, 0xFF, // 65504.0
+ 0xF9, 0x7B, 0xFF, // largest half-precision 65504.0
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,
- 0xF9, 0x7C, 0x00, // Infinity
+ 0xF9, 0x7C, 0x00, // Infinity
0x72,
0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
- 0xF9, 0x00, 0x01, // 0.000000059604
- 0x6F,
- 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
- 0x72, 0x6D, 0x61, 0x6C,
- 0xF9, 0x03, 0xFF, // 0.0000609755516
+ 0xF9, 0x00, 0x01, // Smallest half-precision subnormal 0.000000059604645
0x71,
0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62,
0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
- 0xF9, 0x04, 0x00, // 0.000061988
+ 0xF9, 0x03, 0xFF, // Largest half-precision subnormal 0.0000609755516
+ 0x6F,
+ 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
+ 0x72, 0x6D, 0x61, 0x6C,
+ 0xF9, 0x04, 0x00, // Smallest half-precision normal 0.000061988
0x70,
0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x73,
0x69, 0x6E, 0x67, 0x6C, 0x65,
@@ -74,118 +99,130 @@
0xF9, 0x7E, 0x0F, // qNaN with payload 0x0f
0x07,
0xF9, 0x7C, 0x0F, // sNaN with payload 0x0f
-
};
+inline static bool CheckDouble(double d, uint64_t u)
+{
+ return UsefulBufUtil_CopyDoubleToUint64(d) != u;
+}
+
+
int32_t HalfPrecisionDecodeBasicTests()
{
- UsefulBufC HalfPrecision = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedHalf);
+ UsefulBufC HalfPrecision = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedHalf);
- QCBORDecodeContext DC;
- QCBORDecode_Init(&DC, HalfPrecision, 0);
+ QCBORDecodeContext DC;
+ QCBORDecode_Init(&DC, HalfPrecision, 0);
- QCBORItem Item;
+ QCBORItem Item;
- QCBORDecode_GetNext(&DC, &Item);
- if(Item.uDataType != QCBOR_TYPE_MAP) {
- return -1;
- }
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_MAP) {
+ return -1;
+ }
- QCBORDecode_GetNext(&DC, &Item);
- if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0F) {
- return -2;
- }
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0) {
+ return -2;
+ }
- QCBORDecode_GetNext(&DC, &Item);
- if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
- return -3;
- }
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
+ return -3;
+ }
- QCBORDecode_GetNext(&DC, &Item);
- if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -INFINITY) {
- return -4;
- }
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -INFINITY) {
+ return -4;
+ }
- // TODO, is this really converting right? It is carrying payload, but
- // this confuses things.
- QCBORDecode_GetNext(&DC, &Item);
- if(Item.uDataType != QCBOR_TYPE_DOUBLE || !isnan(Item.val.dfnum)) {
- return -5;
- }
+ // TODO: NAN-related is this really converting right? It is carrying
+ // payload, but this confuses things.
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || !isnan(Item.val.dfnum)) {
+ return -5;
+ }
- QCBORDecode_GetNext(&DC, &Item);
- if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 1.0F) {
- return -6;
- }
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 1.0) {
+ return -6;
+ }
- QCBORDecode_GetNext(&DC, &Item);
- if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.333251953125F) {
- return -7;
- }
+ // Approximately 1/3
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.333251953125) {
+ return -7;
+ }
- QCBORDecode_GetNext(&DC, &Item);
- if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 65504.0F) {
- return -8;
- }
+ // Largest half-precision
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 65504.0) {
+ return -8;
+ }
- QCBORDecode_GetNext(&DC, &Item);
- if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
- return -9;
- }
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
+ return -9;
+ }
- QCBORDecode_GetNext(&DC, &Item); // TODO: check this
- if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000000596046448F) {
- return -10;
- }
+ // Smallest half-precision subnormal
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.00000005960464477539063) {
+ return -10;
+ }
- QCBORDecode_GetNext(&DC, &Item); // TODO: check this
- if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000609755516F) {
- return -11;
- }
+ // Largest half-precision subnormal
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.00006097555160522461) {
+ return -11;
+ }
- QCBORDecode_GetNext(&DC, &Item); // TODO check this
- if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000610351563F) {
- return -12;
- }
+ // Smallest half-precision normal
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.00006103515625) {
+ return -12;
+ }
- QCBORDecode_GetNext(&DC, &Item);
- if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0) {
- return -13;
- }
+ // half-precision zero
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0) {
+ return -13;
+ }
- QCBORDecode_GetNext(&DC, &Item);
- if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -2.0F) {
- return -14;
- }
+ // negative 2
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -2.0) {
+ return -14;
+ }
- // TODO: double check these four tests
- QCBORDecode_GetNext(&DC, &Item); // qNaN
- if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
- UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff8000000000000ULL) {
- return -15;
- }
- QCBORDecode_GetNext(&DC, &Item); // sNaN
- if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
- UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff0000000000001ULL) {
- return -16;
- }
- QCBORDecode_GetNext(&DC, &Item); // qNaN with payload 0x0f
- if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
- UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff800000000000fULL) {
- return -17;
- }
- QCBORDecode_GetNext(&DC, &Item); // sNaN with payload 0x0f
- if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
- UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff000000000000fULL) {
- return -18;
- }
+ // TODO: NAN-related double check these four tests
+ QCBORDecode_GetNext(&DC, &Item); // qNaN
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
+ CheckDouble(Item.val.dfnum, 0x7ff8000000000000ULL)) {
+ return -15;
+ }
+ QCBORDecode_GetNext(&DC, &Item); // sNaN
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
+ CheckDouble(Item.val.dfnum, 0x7ff0000000000001ULL)) {
+ return -16;
+ }
+ QCBORDecode_GetNext(&DC, &Item); // qNaN with payload 0x0f
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
+ CheckDouble(Item.val.dfnum, 0x7ff800000000000fULL)) {
+ return -17;
+ }
+ QCBORDecode_GetNext(&DC, &Item); // sNaN with payload 0x0f
+ if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
+ CheckDouble(Item.val.dfnum, 0x7ff000000000000fULL)) {
+ return -18;
+ }
- if(QCBORDecode_Finish(&DC)) {
- return -19;
- }
+ if(QCBORDecode_Finish(&DC)) {
+ return -19;
+ }
- return 0;
+ return 0;
}
@@ -241,210 +278,601 @@
/*
- {"zero": 0.0,
- "negative zero": -0.0,
- "infinitity": Infinity,
- "negative infinitity": -Infinity,
- "NaN": NaN,
- "one": 1.0,
- "one third": 0.333251953125,
- "largest half-precision": 65504.0,
- "largest half-precision point one": 65504.1,
- "too-large half-precision": 65536.0,
- "smallest subnormal": 5.96046448e-8,
- "smallest normal": 0.00006103515261202119,
- "biggest subnormal": 0.00006103515625,
- "subnormal single": 4.00000646641519e-40,
- 3: -2.0,
- "large single exp": 2.5521177519070385e+38,
- "too-large single exp": 5.104235503814077e+38,
- "biggest single with prec": 16777216.0,
- "first single with prec loss": 16777217.0,
- 1: "fin"}
+ Expected output from preferred serialization of some of floating-point numbers
+{"zero": 0.0,
+ "negative zero": -0.0,
+ "infinitity": Infinity,
+ "negative infinitity": -Infinity,
+ "NaN": NaN,
+ "one": 1.0,
+ "one third": 0.333251953125,
+ "largest half-precision": 65504.0,
+ "largest half-precision point one": 65504.1,
+ "too-large half-precision": 65536.0,
+ "smallest half subnormal": 5.960464477539063e-8,
+ "smallest half normal": 0.00006103515625,
+ "smallest half normal plus": 0.00006103515625000001,
+ "smallest normal minus": 0.000030517578125,
+ "largest single": 3.4028234663852886e+38,
+ "largest single plus": 6.805646932770577e+38,
+ "smallest single": 1.1754943508222875e-38,
+ "smallest single plus": 1.1754943508222878e-38,
+ "smallest single minus": 1.1754943508222874e-38,
+ "smallest single minus more": 5.877471754111438e-39,
+ 3: -2.0, "single precision": 16777216.0,
+ "single with precision loss": 16777217.0,
+ 1: "fin"}
*/
static const uint8_t spExpectedSmallest[] = {
- 0xB4, 0x64, 0x7A, 0x65, 0x72, 0x6F, 0xF9, 0x00, 0x00, 0x6D,
- 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x7A,
- 0x65, 0x72, 0x6F, 0xF9, 0x80, 0x00, 0x6A, 0x69, 0x6E, 0x66,
- 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79, 0xF9, 0x7C, 0x00,
- 0x73, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20,
- 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
- 0xF9, 0xFC, 0x00, 0x63, 0x4E, 0x61, 0x4E, 0xF9, 0x7E, 0x00,
- 0x63, 0x6F, 0x6E, 0x65, 0xF9, 0x3C, 0x00, 0x69, 0x6F, 0x6E,
- 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0xF9, 0x35, 0x55,
- 0x76, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68,
- 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73,
- 0x69, 0x6F, 0x6E, 0xF9, 0x7B, 0xFF, 0x78, 0x20, 0x6C, 0x61,
- 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66,
- 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
- 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, 0x6F, 0x6E, 0x65,
- 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33, 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, 0xFA, 0x47, 0x80, 0x00, 0x00,
- 0x72, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
- 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xFB,
- 0x3E, 0x70, 0x00, 0x00, 0x00, 0x1C, 0x5F, 0x68, 0x6F, 0x73,
- 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
- 0x72, 0x6D, 0x61, 0x6C, 0xFA, 0x38, 0x7F, 0xFF, 0xFF, 0x71,
- 0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
- 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xF9, 0x04, 0x00,
- 0x70, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
- 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0xFB, 0x37, 0xC1,
- 0x6C, 0x28, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF9, 0xC0, 0x00,
- 0x70, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E,
- 0x67, 0x6C, 0x65, 0x20, 0x65, 0x78, 0x70, 0xFA, 0x7F, 0x40,
- 0x00, 0x00, 0x74, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72,
- 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20,
- 0x65, 0x78, 0x70, 0xFB, 0x47, 0xF8, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x78, 0x18, 0x62, 0x69, 0x67, 0x67, 0x65, 0x73,
- 0x74, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77,
- 0x69, 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0xFA, 0x4B,
- 0x80, 0x00, 0x00, 0x78, 0x1B, 0x66, 0x69, 0x72, 0x73, 0x74,
- 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77, 0x69,
- 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x20, 0x6C, 0x6F,
- 0x73, 0x73, 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00,
- 0x00, 0x01, 0x63, 0x66, 0x69, 0x6E
+ 0xB8, 0x1A,
+ 0x64, 0x7A, 0x65, 0x72, 0x6F,
+ 0xF9, 0x00, 0x00,
+
+ 0x6D, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x7A,
+ 0x65, 0x72, 0x6F,
+ 0xF9, 0x80, 0x00,
+
+ 0x6A, 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
+ 0xF9, 0x7C, 0x00,
+
+ 0x73, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69,
+ 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
+ 0xF9, 0xFC, 0x00,
+
+ 0x63, 0x4E, 0x61, 0x4E,
+ 0xF9, 0x7E, 0x00,
+
+ 0x63, 0x6F, 0x6E, 0x65,
+ 0xF9, 0x3C, 0x00,
+
+ 0x69, 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
+ 0xF9, 0x35, 0x55,
+
+ 0x76, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61,
+ 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69,
+ 0x6F, 0x6E,
+ 0xF9, 0x7B, 0xFF,
+
+ 0x78, 0x20, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68,
+ 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73,
+ 0x69, 0x6F, 0x6E, 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20,
+ 0x6F, 0x6E, 0x65,
+ 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33,
+
+ 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,
+ 0xFA, 0x47, 0x80, 0x00, 0x00,
+
+ 0x77, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74,
+ 0x20, 0x68, 0x61, 0x6C, 0x66, 0x20, 0x73, 0x75, 0x62, 0x6E,
+ 0x6F, 0x72, 0x6D, 0x61, 0x6C,
+ 0xFA, 0x33, 0x80, 0x00, 0x00,
+
+ 0x74, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x68,
+ 0x61, 0x6C, 0x66, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
+ 0xF9, 0x04, 0x00,
+
+ 0x78, 0x19, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
+ 0x68, 0x61, 0x6C, 0x66, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61,
+ 0x6C, 0x20, 0x70, 0x6C, 0x75, 0x73,
+ 0xFB, 0x3F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+
+ 0x75, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E,
+ 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x6D, 0x69, 0x6E,
+ 0x75, 0x73,
+ 0xFB, 0x3F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+
+ 0x75, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E,
+ 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x6D, 0x69, 0x6E, 0x75,
+ 0x73,
+ 0xFA, 0x38, 0x00, 0x00, 0x00,
+
+ 0x6E, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69,
+ 0x6E, 0x67, 0x6C, 0x65,
+ 0xFA, 0x7F, 0x7F, 0xFF, 0xFF,
+
+ 0x73, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69,
+ 0x6E,0x67, 0x6C, 0x65, 0x20, 0x70, 0x6C, 0x75, 0x73,
+ 0xFB, 0x47, 0xEF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x01,
+
+ 0x73, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69,
+ 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x70, 0x6C, 0x75, 0x73,
+ 0xFB, 0x47, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
+
+ 0x6F, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73,
+ 0x69, 0x6E, 0x67, 0x6C, 0x65,
+ 0xFA, 0x00, 0x80, 0x00, 0x00,
+
+ 0x74, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73,
+ 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x70, 0x6C, 0x75, 0x73,
+ 0xFB, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+
+ 0x75, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73,
+ 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x6D, 0x69, 0x6E, 0x75,
+ 0x73,
+ 0xFB, 0x38, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+
+ 0x78, 0x1A, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
+ 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x6D, 0x69, 0x6E,
+ 0x75, 0x73, 0x20, 0x6D, 0x6F, 0x72, 0x65,
+ 0xFB, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x03,
+ 0xF9, 0xC0, 0x00,
+
+ 0x70, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x70, 0x72, 0x65,
+ 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
+ 0xFA, 0x4B, 0x80, 0x00, 0x00,
+
+ 0x78, 0x1A, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69,
+ 0x6F, 0x6E, 0x20, 0x6C, 0x6F, 0x73, 0x73,
+ 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+
+ 0x01,
+ 0x63, 0x66, 0x69, 0x6E
};
+/*
+ Makes a double from a uint64_t by copying the bits, not
+ by converting the value.
+ */
+#define MAKE_DOUBLE(x) UsefulBufUtil_CopyUint64ToDouble(x)
+
+
int32_t DoubleAsSmallestTest()
{
- UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, 420);
+ UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, sizeof(spExpectedSmallest));
-#define QCBOREncode_AddDoubleAsSmallestToMap QCBOREncode_AddDoubleToMap
-#define QCBOREncode_AddDoubleAsSmallestToMapN QCBOREncode_AddDoubleToMapN
+ QCBOREncodeContext EC;
+ QCBOREncode_Init(&EC, EncodedHalfsMem);
+ QCBOREncode_OpenMap(&EC);
+
+ // Many of these are from
+ // https://en.wikipedia.org/wiki/Half-precision_floating-point_format
+ // and
+ // https://en.wikipedia.org/wiki/Single-precision_floating-point_format
+
+ // F9 0000 # primitive(0)
+ QCBOREncode_AddDoubleToMap(&EC, "zero", 0.00);
+
+ // F9 8000 # primitive(0)
+ QCBOREncode_AddDoubleToMap(&EC, "negative zero", -0.00);
+
+ // F9 7C00 # primitive(31744)
+ QCBOREncode_AddDoubleToMap(&EC, "infinitity", INFINITY);
+
+ // F9 FC00 # primitive(64512)
+ QCBOREncode_AddDoubleToMap(&EC, "negative infinitity", -INFINITY);
+
+ // F9 7E00 # primitive(32256)
+ QCBOREncode_AddDoubleToMap(&EC, "NaN", NAN);
+
+ // TODO: test a few NaN variants
+
+ // F9 3C00 # primitive(15360)
+ QCBOREncode_AddDoubleToMap(&EC, "one", 1.0);
+
+ // F9 3555 # primitive(13653)
+ QCBOREncode_AddDoubleToMap(&EC, "one third", 0.333251953125);
+
+ // 65504.0, converts to the large possible half-precision.
+ // 0xF9, 0x7B, 0xFF,
+ QCBOREncode_AddDoubleToMap(&EC, "largest half-precision", 65504.0);
+
+ // 65504.1, the double that has both to large an exponent and too
+ // much precision, so no conversion.
+ // 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33,
+ QCBOREncode_AddDoubleToMap(&EC, "largest half-precision point one", 65504.1);
+
+ // 65536.0 has an exponent of 16, which is larger than 15, the
+ // largest half-precision exponent. It is the exponent, not
+ // precision loss that prevents conversion to half. It does convert
+ // to single precision.
+ // 0xFA, 0x47, 0x80, 0x00, 0x00,
+ QCBOREncode_AddDoubleToMap(&EC, "too-large half-precision", 65536.0);
+
+ // 5.9604644775390625E-8, the smallest possible half-precision
+ // subnormal, digitis are lost converting to half, but not
+ // when converting to a single
+ // 0xFA, 0x33, 0x80, 0x00, 0x00,
+ QCBOREncode_AddDoubleToMap(&EC,
+ "smallest half subnormal",
+ MAKE_DOUBLE(0x3e70000000000000));
+
+ // 0.00006103515625, the double value that converts to the smallest
+ // possible half-precision normal. which is what should appear in
+ // the output.
+ // 0xF9, 0x04, 0x00,
+ QCBOREncode_AddDoubleToMap(&EC,
+ "smallest half normal",
+ MAKE_DOUBLE(0x3f10000000000000));
+
+ // 0.000061035156250000014 ,the double value that is a tiny bit
+ // greater than smallest possible half-precision normal. It will be
+ // output as a double because converting it will reduce precision.
+ // 0xFB, 0x3F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ QCBOREncode_AddDoubleToMap(&EC,
+ "smallest half normal plus",
+ MAKE_DOUBLE(0x3f10000000000001));
+
+ // 0.000061035156249999993, the double value that is a tiny bit
+ // smaller than the smallest half-precision normal. This will fail
+ // to convert to a half-precision because both the exponent is too
+ // small and the precision is too large for a half-precision.
+ // 0xFB, 0x3F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ QCBOREncode_AddDoubleToMap(&EC,
+ "smallest normal minus",
+ MAKE_DOUBLE(0x3f0fffffffffffff));
+
+ // 0.000030517578125, the double value that is too small to fit
+ // into a half-precision because the exponent won't fit, not
+ // because precision would be lost. (This would fit into a
+ // half-precision subnormal, but there is no converstion to
+ // that). This ends up encoded as a single-precision.
+ // 0xFA, 0x38, 0x00, 0x00, 0x00,
+ QCBOREncode_AddDoubleToMap(&EC,
+ "smallest normal minus",
+ MAKE_DOUBLE(0x3f00000000000000));
+
+ // 3.4028234664e38, the value that converts to the largest possible
+ // single-precision.
+ // 0xFA, 0x7F, 0x7F, 0xFF, 0xFF,
+ QCBOREncode_AddDoubleToMap(&EC,
+ "largest single",
+ MAKE_DOUBLE(0x47efffffe0000000));
+
+ // 3.402823466385289E38, sightly larger than the largest possible
+ // possible precision. Conversion fails because precision would be
+ // lost.
+ // 0xFB, 0x47, 0xEF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x01,
+ QCBOREncode_AddDoubleToMap(&EC,
+ "largest single plus",
+ MAKE_DOUBLE(0x47efffffe0000001));
+
+ // 6.8056469327705772E38, slightly more larger than the largers
+ // possible single precision. Conversion fails because exponent is
+ // too large.
+ // 0xFB, 0x47, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
+ QCBOREncode_AddDoubleToMap(&EC,
+ "largest single plus",
+ MAKE_DOUBLE(0x47ffffffe0000000));
+
+ // 1.1754943508222875E-38, The double value that converts to the
+ // smallest possible single-precision normal
+ // 0xFA, 0x00, 0x80, 0x00, 0x00,
+ QCBOREncode_AddDoubleToMap(&EC,
+ "smallest single",
+ MAKE_DOUBLE(0x3810000000000000));
+
+ // 1.1754943508222878E-38, double value that is slightly larger
+ // than the smallest single-precision normal. Conversion fails
+ // because of precision
+ // 0xFB, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ QCBOREncode_AddDoubleToMap(&EC,
+ "smallest single plus",
+ MAKE_DOUBLE(0x3810000000000001));
+
+ // 1.1754943508222874E-38, slightly smaller than the smallest
+ // single-precision normal. Conversion fails because of precision
+ // 0xFB, 0x38, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ QCBOREncode_AddDoubleToMap(&EC,
+ "smallest single minus",
+ MAKE_DOUBLE(0x380fffffffffffff));
+
+ // 5.8774717541114375E-39, slightly smaller than the smallest
+ // single-precision normal. Conversion fails because the exponent
+ // is too small.
+ // 0xFB, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ QCBOREncode_AddDoubleToMap(&EC,
+ "smallest single minus more",
+ MAKE_DOUBLE(0x3800000000000000));
+
+ // Just -2, which converts to a negative half-precision
+ // F9 C000 # primitive(49152)
+ QCBOREncode_AddDoubleToMapN(&EC, 3, -2.0);
+
+ // 16777216, No precision loss converting to single
+ // FA 4B800000 # primitive(1266679808)
+ QCBOREncode_AddDoubleToMap(&EC, "single precision", 16777216);
+
+ // 16777217, One more than above. Too much precision for a single
+ // so no conversion.
+ // 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ QCBOREncode_AddDoubleToMap(&EC, "single with precision loss", 16777217);
+
+ // Just a convenient marker when cutting and pasting encoded CBOR
+ QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
+
+ QCBOREncode_CloseMap(&EC);
+
+ UsefulBufC EncodedHalfs;
+ QCBORError uErr = QCBOREncode_Finish(&EC, &EncodedHalfs);
+ if(uErr) {
+ return -1;
+ }
+
+ if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedSmallest))) {
+ return -3;
+ }
+
+ return 0;
+}
+#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
- QCBOREncodeContext EC;
- QCBOREncode_Init(&EC, EncodedHalfsMem);
- // These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
- QCBOREncode_OpenMap(&EC);
- // 64 # text(4)
- // 7A65726F # "zero"
- // F9 0000 # primitive(0)
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "zero", 0.00);
+/*
+[0.0, 3.14, 0.0, NaN, Infinity, 0.0, 3.140000104904175, 0.0, NaN, Infinity,
+ {100: 0.0, 101: 3.1415926, "euler": 2.718281828459045, 105: 0.0,
+ 102: 0.0, 103: 3.141592502593994, "euler2": 2.7182817459106445, 106: 0.0}]
+ */
+static const uint8_t spExpectedFloats[] = {
+ 0x8B,
+ 0xF9, 0x00, 0x00,
+ 0xFB, 0x40, 0x09, 0x1E, 0xB8, 0x51, 0xEB, 0x85, 0x1F,
+ 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xF9, 0x00, 0x00,
+ 0xFA, 0x40, 0x48, 0xF5, 0xC3,
+ 0xFA, 0x00, 0x00, 0x00, 0x00,
+ 0xFA, 0x7F, 0xC0, 0x00, 0x00,
+ 0xFA, 0x7F, 0x80, 0x00, 0x00,
+ 0xA8,
+ 0x18, 0x64,
+ 0xF9, 0x00, 0x00,
+ 0x18, 0x65,
+ 0xFB, 0x40, 0x09, 0x21, 0xFB, 0x4D, 0x12, 0xD8, 0x4A,
+ 0x65, 0x65, 0x75, 0x6C, 0x65, 0x72,
+ 0xFB, 0x40, 0x05, 0xBF, 0x0A, 0x8B, 0x14, 0x57, 0x69,
+ 0x18, 0x69,
+ 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x66,
+ 0xF9, 0x00, 0x00,
+ 0x18, 0x67,
+ 0xFA, 0x40, 0x49, 0x0F, 0xDA,
+ 0x66, 0x65, 0x75, 0x6C, 0x65, 0x72, 0x32,
+ 0xFA, 0x40, 0x2D, 0xF8, 0x54,
+ 0x18, 0x6A,
+ 0xFA, 0x00, 0x00, 0x00, 0x00};
- // 64 # text(4)
- // 7A65726F # "negative zero"
- // F9 8000 # primitive(0)
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative zero", -0.00);
+static const uint8_t spExpectedFloatsNoHalf[] = {
+ 0x8B,
+ 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFB, 0x40, 0x09, 0x1E, 0xB8, 0x51, 0xEB, 0x85, 0x1F,
+ 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFA, 0x00, 0x00, 0x00, 0x00,
+ 0xFA, 0x40, 0x48, 0xF5, 0xC3,
+ 0xFA, 0x00, 0x00, 0x00, 0x00,
+ 0xFA, 0x7F, 0xC0, 0x00, 0x00,
+ 0xFA, 0x7F, 0x80, 0x00, 0x00,
+ 0xA8,
+ 0x18, 0x64,
+ 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x65,
+ 0xFB, 0x40, 0x09, 0x21, 0xFB, 0x4D, 0x12, 0xD8, 0x4A,
+ 0x65, 0x65, 0x75, 0x6C, 0x65, 0x72,
+ 0xFB, 0x40, 0x05, 0xBF, 0x0A, 0x8B, 0x14, 0x57, 0x69,
+ 0x18, 0x69,
+ 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x66,
+ 0xFA, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x67,
+ 0xFA, 0x40, 0x49, 0x0F, 0xDA,
+ 0x66, 0x65, 0x75, 0x6C, 0x65, 0x72, 0x32,
+ 0xFA, 0x40, 0x2D, 0xF8, 0x54,
+ 0x18, 0x6A,
+ 0xFA, 0x00, 0x00, 0x00, 0x00};
- // 6A # text(10)
- // 696E66696E6974697479 # "infinitity"
- // F9 7C00 # primitive(31744)
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "infinitity", INFINITY);
+int32_t GeneralFloatEncodeTests()
+{
+ UsefulBufC ExpectedFloats;
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+ UsefulBuf_MAKE_STACK_UB(OutBuffer, sizeof(spExpectedFloats));
+ ExpectedFloats = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloats);
+ (void)spExpectedFloatsNoHalf; // Avoid unused variable error
+#else
+ UsefulBuf_MAKE_STACK_UB(OutBuffer, sizeof(spExpectedFloatsNoHalf));
+ ExpectedFloats = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloatsNoHalf);
+ (void)spExpectedFloats; // Avoid unused variable error
+#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
- // 73 # text(19)
- // 6E6567617469766520696E66696E6974697479 # "negative infinitity"
- // F9 FC00 # primitive(64512)
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative infinitity", -INFINITY);
+ QCBOREncodeContext EC;
+ QCBOREncode_Init(&EC, OutBuffer);
+ QCBOREncode_OpenArray(&EC);
- // 63 # text(3)
- // 4E614E # "NaN"
- // F9 7E00 # primitive(32256)
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "NaN", NAN);
+ QCBOREncode_AddDouble(&EC, 0.0);
+ QCBOREncode_AddDouble(&EC, 3.14);
+ QCBOREncode_AddDoubleNoPreferred(&EC, 0.0);
+ QCBOREncode_AddDoubleNoPreferred(&EC, NAN);
+ QCBOREncode_AddDoubleNoPreferred(&EC, INFINITY);
- // TODO: test a few NaN variants
+ QCBOREncode_AddFloat(&EC, 0.0);
+ QCBOREncode_AddFloat(&EC, 3.14f);
+ QCBOREncode_AddFloatNoPreferred(&EC, 0.0f);
+ QCBOREncode_AddFloatNoPreferred(&EC, NAN);
+ QCBOREncode_AddFloatNoPreferred(&EC, INFINITY);
- // 63 # text(3)
- // 6F6E65 # "one"
- // F9 3C00 # primitive(15360)
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one", 1.0);
+ QCBOREncode_OpenMap(&EC);
- // 69 # text(9)
- // 6F6E65207468697264 # "one third"
- // F9 3555 # primitive(13653)
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one third", 0.333251953125);
+ QCBOREncode_AddDoubleToMapN(&EC, 100, 0.0);
+ QCBOREncode_AddDoubleToMapN(&EC, 101, 3.1415926);
+ QCBOREncode_AddDoubleToMap(&EC, "euler", 2.71828182845904523536);
+ QCBOREncode_AddDoubleNoPreferredToMapN(&EC, 105, 0.0);
- // 76 # text(22)
- // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
- // F9 7BFF # primitive(31743)
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision",65504.0);
+ QCBOREncode_AddFloatToMapN(&EC, 102, 0.0f);
+ QCBOREncode_AddFloatToMapN(&EC, 103, 3.1415926f);
+ QCBOREncode_AddFloatToMap(&EC, "euler2", 2.71828182845904523536f);
+ QCBOREncode_AddFloatNoPreferredToMapN(&EC, 106, 0.0f);
- // 76 # text(22)
- // 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
- // F9 7BFF # primitive(31743)
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision point one",65504.1);
+ QCBOREncode_CloseMap(&EC);
+ QCBOREncode_CloseArray(&EC);
- // Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which
- // is larger than 15, the largest half-precision exponent
- // 78 18 # text(24)
- // 746F6F2D6C617267652068616C662D707265636973696F6E # "too-large half-precision"
- // FA 47800000 # primitive(31743)
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large half-precision", 65536.0);
+ UsefulBufC Encoded;
+ QCBORError uErr = QCBOREncode_Finish(&EC, &Encoded);
+ if(uErr) {
+ return -1;
+ }
- // The smallest possible half-precision subnormal, but digitis are lost converting
- // to half, so this turns into a double
- // 72 # text(18)
- // 736D616C6C657374207375626E6F726D616C # "smallest subnormal"
- // FB 3E700000001C5F68 # primitive(4499096027744984936)
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest subnormal", 0.0000000596046448);
+ if(UsefulBuf_Compare(Encoded, ExpectedFloats)) {
+ return -3;
+ }
- // The smallest possible half-precision snormal, but digitis are lost converting
- // to half, so this turns into a single TODO: confirm this is right
- // 6F # text(15)
- // 736D616C6C657374206E6F726D616C # "smallest normal"
- // FA 387FFFFF # primitive(947912703)
- // in hex single is 0x387fffff, exponent -15, significand 7fffff
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest normal", 0.0000610351526F);
+ return 0;
+}
- // 71 # text(17)
- // 62696767657374207375626E6F726D616C # "biggest subnormal"
- // F9 0400 # primitive(1024)
- // in hex single is 0x38800000, exponent -14, significand 0
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest subnormal", 0.0000610351563F);
- // 70 # text(16)
- // 7375626E6F726D616C2073696E676C65 # "subnormal single"
- // FB 37C16C2800000000 # primitive(4017611261645684736)
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "subnormal single", 4e-40F);
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+/* returns 0 if equivalent, non-zero if not equivalent */
+static int CHECK_EXPECTED_DOUBLE(double val, double expected)
+{
+ double diff = val - expected;
- // 03 # unsigned(3)
- // F9 C000 # primitive(49152)
- QCBOREncode_AddDoubleAsSmallestToMapN(&EC, 3, -2.0);
+ diff = fabs(diff);
- // 70 # text(16)
- // 6C617267652073696E676C6520657870 # "large single exp"
- // FA 7F400000 # primitive(2134900736)
- // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((127LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "large single exp", 2.5521177519070385E+38); // Exponent fits single
+ if(diff > 0.000001) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+#endif
- // 74 # text(20)
- // 746F6F2D6C617267652073696E676C6520657870 # "too-large single exp"
- // FB 47F8000000000000 # primitive(5185894970917126144)
- // (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((128LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
- // Exponent too large for single
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large single exp", 5.104235503814077E+38);
- // 66 # text(6)
- // 646664666465 # "dfdfde"
- // FA 4B800000 # primitive(1266679808)
- // Single with no precision loss
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest single with prec", 16777216);
+int32_t GeneralFloatDecodeTests()
+{
+ UsefulBufC TestData = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloats);
- // 78 18 # text(24)
- // 626967676573742073696E676C6520776974682070726563 # "biggest single with prec"
- // FA 4B800000 # primitive(1266679808)
- // Double becuase of precision loss
- QCBOREncode_AddDoubleAsSmallestToMap(&EC, "first single with prec loss", 16777217);
+ QCBORDecodeContext DC;
+ QCBORDecode_Init(&DC, TestData, 0);
- // Just a convenient marker when cutting and pasting encoded CBOR
- QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
+ QCBORItem Item;
+ QCBORError uErr;
- QCBOREncode_CloseMap(&EC);
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -1;
+ }
- UsefulBufC EncodedHalfs;
- QCBORError nReturn = QCBOREncode_Finish(&EC, &EncodedHalfs);
- if(nReturn != QCBOR_SUCCESS) {
- return -1;
- }
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+ uErr = QCBORDecode_GetNext(&DC, &Item);
+ if(uErr != QCBOR_SUCCESS ||
+ Item.uDataType != QCBOR_TYPE_DOUBLE ||
+ Item.val.dfnum != 0.0) {
+ return -2;
+ }
+#else
+ uErr = QCBORDecode_GetNext(&DC, &Item);
+ if(uErr != QCBOR_ERR_HALF_PRECISION_UNSUPPORTED) {
+ return -3;
+ }
+#endif
- if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedSmallest))) {
- return -3;
- }
+ uErr = QCBORDecode_GetNext(&DC, &Item);
+ if(uErr != QCBOR_SUCCESS ||
+ Item.uDataType != QCBOR_TYPE_DOUBLE ||
+ Item.val.dfnum != 3.14) {
+ return -4;
+ }
- return 0;
+ uErr = QCBORDecode_GetNext(&DC, &Item);
+ if(uErr != QCBOR_SUCCESS ||
+ Item.uDataType != QCBOR_TYPE_DOUBLE ||
+ Item.val.dfnum != 0.0) {
+ return -5;
+ }
+
+ uErr = QCBORDecode_GetNext(&DC, &Item);
+ if(uErr != QCBOR_SUCCESS ||
+ Item.uDataType != QCBOR_TYPE_DOUBLE ||
+ !isnan(Item.val.dfnum)) {
+ return -6;
+ }
+
+ uErr = QCBORDecode_GetNext(&DC, &Item);
+ if(uErr != QCBOR_SUCCESS ||
+ Item.uDataType != QCBOR_TYPE_DOUBLE ||
+ Item.val.dfnum != INFINITY) {
+ return -7;
+ }
+
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+ uErr = QCBORDecode_GetNext(&DC, &Item);
+ if(uErr != QCBOR_SUCCESS ||
+ Item.uDataType != QCBOR_TYPE_DOUBLE ||
+ Item.val.dfnum != 0.0) {
+ return -8;
+ }
+
+ uErr = QCBORDecode_GetNext(&DC, &Item);
+ if(uErr != QCBOR_SUCCESS ||
+ Item.uDataType != QCBOR_TYPE_DOUBLE ||
+ CHECK_EXPECTED_DOUBLE(3.14, Item.val.dfnum)) {
+ return -9;
+ }
+
+ uErr = QCBORDecode_GetNext(&DC, &Item);
+ if(uErr != QCBOR_SUCCESS ||
+ Item.uDataType != QCBOR_TYPE_DOUBLE ||
+ Item.val.dfnum != 0.0) {
+ return -10;
+ }
+
+ uErr = QCBORDecode_GetNext(&DC, &Item);
+ if(uErr != QCBOR_SUCCESS ||
+ Item.uDataType != QCBOR_TYPE_DOUBLE ||
+ !isnan(Item.val.dfnum)) {
+ return -11;
+ }
+
+ uErr = QCBORDecode_GetNext(&DC, &Item);
+ if(uErr != QCBOR_SUCCESS ||
+ Item.uDataType != QCBOR_TYPE_DOUBLE ||
+ Item.val.dfnum != INFINITY) {
+ return -12;
+ }
+
+#else
+ uErr = QCBORDecode_GetNext(&DC, &Item);
+ if(uErr != QCBOR_ERR_HALF_PRECISION_UNSUPPORTED) {
+ return -13;
+ }
+
+ uErr = QCBORDecode_GetNext(&DC, &Item);
+ if(uErr != QCBOR_SUCCESS ||
+ Item.uDataType != QCBOR_TYPE_FLOAT ||
+ Item.val.fnum != 3.14f) {
+ return -14;
+ }
+
+ uErr = QCBORDecode_GetNext(&DC, &Item);
+ if(uErr != QCBOR_SUCCESS ||
+ Item.uDataType != QCBOR_TYPE_FLOAT ||
+ Item.val.fnum != 0.0f) {
+ return -15;
+ }
+
+ uErr = QCBORDecode_GetNext(&DC, &Item);
+ if(uErr != QCBOR_SUCCESS ||
+ Item.uDataType != QCBOR_TYPE_FLOAT ||
+ !isnan(Item.val.fnum)) {
+ return -16;
+ }
+
+ uErr = QCBORDecode_GetNext(&DC, &Item);
+ if(uErr != QCBOR_SUCCESS ||
+ Item.uDataType != QCBOR_TYPE_FLOAT ||
+ Item.val.fnum != INFINITY) {
+ return -17;
+ }
+#endif
+ /* Sufficent test coverage. Don't need to decode the rest */
+
+ return 0;
}
@@ -491,6 +919,3 @@
return 0;
}
#endif
-
-
-
diff --git a/test/float_tests.h b/test/float_tests.h
index f777156..54daa3f 100644
--- a/test/float_tests.h
+++ b/test/float_tests.h
@@ -15,11 +15,26 @@
#include <stdint.h>
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+
int32_t HalfPrecisionDecodeBasicTests(void);
int32_t DoubleAsSmallestTest(void);
int32_t HalfPrecisionAgainstRFCCodeTest(void);
+#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
+
+/*
+ This calls each and every method for encoding
+ floating-point numbers.
+ */
+int32_t GeneralFloatEncodeTests(void);
+
+/*
+ Tests basic float decoding.
+ */
+int32_t GeneralFloatDecodeTests(void);
+
#endif /* float_tests_h */
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index deb4c6c..c3dcc1e 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -372,7 +372,7 @@
return -1;
- if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
+ if(( nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
return (int32_t)nCBORError;
if(Item.uDataType != QCBOR_TYPE_INT64 ||
Item.val.int64 != 4294967295)
@@ -1276,19 +1276,19 @@
UsefulBuf_Compare(Item.val.string, UsefulBuf_FromSZ("lies, damn lies and statistics"))) {
return -17;
}
-
-
+
+
/*
Test with map that nearly QCBOR_MAX_ITEMS_IN_ARRAY items in a
map that when interpreted as an array will be too many. Test
data just has the start of the map, not all the items in the map.
*/
static const uint8_t pTooLargeMap[] = {0xb9, 0xff, 0xfd};
-
+
QCBORDecode_Init(&DCtx,
UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pTooLargeMap),
QCBOR_DECODE_MODE_MAP_AS_ARRAY);
-
+
if((QCBOR_ERR_ARRAY_TOO_LONG != QCBORDecode_GetNext(&DCtx, &Item))) {
return -50;
}
@@ -2105,14 +2105,14 @@
0x1a, 0x53, 0x72, 0x4E, 0x00, // Epoch date 1400000000; Tue, 13 May 2014 16:53:20 GMT
// CBOR_TAG_B64
- 0xc1, 0xcf, 0xd8, 0x22, // 0xee, // Epoch date with extra tags TODO: fix this test
+ 0xc1, 0xcf, 0xd8, 0x22, // 0xee, // Epoch date with extra tags
0x1a, 0x53, 0x72, 0x4E, 0x01,
0xc1, // tag for epoch date
0x1b, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // Too large integer
0xc1, // tag for epoch date
- 0xfa, 0x3f, 0x8c, 0xcc, 0xcd, // double with value 1.1
+ 0xfa, 0x3f, 0x8c, 0xcc, 0xcd, // single with value 1.1
0xc1, // tag for epoch date
0xfa, 0x7f, 0x7f, 0xff, 0xff, // 3.4028234663852886e+38 too large
@@ -2127,7 +2127,7 @@
// have to check float expected only to within an epsilon
-int CHECK_EXPECTED_DOUBLE(double val, double expected) {
+static int CHECK_EXPECTED_DOUBLE(double val, double expected) {
double diff = val - expected;
@@ -2160,7 +2160,7 @@
return -2;
}
- // Epoch date
+ // Epoch date 1400000000; Tue, 13 May 2014 16:53:20 GMT
if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
return -3;
if(Item.uDataType != QCBOR_TYPE_DATE_EPOCH ||
@@ -2186,6 +2186,7 @@
}
// Epoch date in float format with fractional seconds
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
return -8;
if(Item.uDataType != QCBOR_TYPE_DATE_EPOCH ||
@@ -2205,12 +2206,28 @@
}
// Largest double epoch date supported
- if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_SUCCESS ||
- Item.uDataType != QCBOR_TYPE_DATE_EPOCH ||
- Item.val.epochDate.nSeconds != 9223372036854773760 ||
- Item.val.epochDate.nSeconds == 0) {
- return -12;
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_SUCCESS ||
+ Item.uDataType != QCBOR_TYPE_DATE_EPOCH ||
+ Item.val.epochDate.nSeconds != 9223372036854773760 ||
+ Item.val.epochDate.nSeconds == 0) {
+ return -12;
+ }
+#else
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_FLOAT_DATE_UNSUPPORTED) {
+ return -80;
}
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_FLOAT_DATE_UNSUPPORTED) {
+ return -80;
+ }
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_FLOAT_DATE_UNSUPPORTED) {
+ return -80;
+ }
+ if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_FLOAT_DATE_UNSUPPORTED) {
+ return -80;
+ }
+#endif
+
+
// TODO: could use a few more tests with float, double, and half precsion
// and negative (but coverage is still pretty good)
@@ -4096,8 +4113,10 @@
[],
[]
]
- }
+ }
*/
+
+
static const uint8_t spMapOfEmpty[] = {
0xa6, 0x00, 0x80, 0x09, 0x82, 0x80, 0x80, 0x08, 0xa3, 0x01,
0x80, 0x02, 0xa0, 0x03, 0x80, 0x04, 0xa0, 0x05, 0x9f, 0xff,
@@ -4826,15 +4845,15 @@
if(uCBORError != QCBOR_SUCCESS) {
return 5;
}
-
-
+
+
// --- Sequence with unclosed indefinite length array ---
static const uint8_t xx[] = {0x01, 0x9f, 0x02};
-
+
QCBORDecode_Init(&DCtx,
UsefulBuf_FROM_BYTE_ARRAY_LITERAL(xx),
QCBOR_DECODE_MODE_NORMAL);
-
+
// Get the first item
uCBORError = QCBORDecode_GetNext(&DCtx, &Item);
if(uCBORError != QCBOR_SUCCESS) {
@@ -4843,7 +4862,7 @@
if(Item.uDataType != QCBOR_TYPE_INT64) {
return 8;
}
-
+
// Get a second item
uCBORError = QCBORDecode_GetNext(&DCtx, &Item);
if(uCBORError != QCBOR_SUCCESS) {
@@ -4860,14 +4879,14 @@
return 11;
}
-
+
// --- Sequence with a closed indefinite length array ---
static const uint8_t yy[] = {0x01, 0x9f, 0xff};
-
+
QCBORDecode_Init(&DCtx,
UsefulBuf_FROM_BYTE_ARRAY_LITERAL(yy),
QCBOR_DECODE_MODE_NORMAL);
-
+
// Get the first item
uCBORError = QCBORDecode_GetNext(&DCtx, &Item);
if(uCBORError != QCBOR_SUCCESS) {
@@ -4876,7 +4895,7 @@
if(Item.uDataType != QCBOR_TYPE_INT64) {
return 13;
}
-
+
// Get a second item
uCBORError = QCBORDecode_GetNext(&DCtx, &Item);
if(uCBORError != QCBOR_SUCCESS) {
@@ -4893,7 +4912,7 @@
return 16;
}
-
+
return 0;
}
diff --git a/test/run_tests.c b/test/run_tests.c
index 794c456..0adccd4 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -94,9 +94,13 @@
TEST_ENTRY(IntegerValuesParseTest),
TEST_ENTRY(MemPoolTest),
TEST_ENTRY(IndefiniteLengthStringTest),
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
TEST_ENTRY(HalfPrecisionDecodeBasicTests),
TEST_ENTRY(DoubleAsSmallestTest),
TEST_ENTRY(HalfPrecisionAgainstRFCCodeTest),
+#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
+ TEST_ENTRY(GeneralFloatEncodeTests),
+ TEST_ENTRY(GeneralFloatDecodeTests),
TEST_ENTRY(BstrWrapTest),
TEST_ENTRY(BstrWrapErrorTest),
TEST_ENTRY(BstrWrapNestTest),