Преглед на файлове

use platform agnostic code for float encoding from beej's guide. (#559)

Julius Pfrommer преди 9 години
родител
ревизия
cfb39b1c66
променени са 2 файла, в които са добавени 81 реда и са изтрити 46 реда
  1. 53 29
      src/ua_types_encoding_binary.c
  2. 28 17
      tests/check_builtin.c

+ 53 - 29
src/ua_types_encoding_binary.c

@@ -184,40 +184,64 @@ DateTime_decodeBinary(bufpos pos, bufend end, UA_DateTime *dst) {
 # define Double_encodeBinary UInt64_encodeBinary
 # define Double_decodeBinary UInt64_decodeBinary
 #else
+
+#include <math.h>
+
+/* Handling of IEEE754 floating point values was taken from Beej's Guide to
+   Network Programming (http://beej.us/guide/bgnet/) and enhanced to cover the
+   edge cases +/-0, +/-inf and nan. */
+
 /* Float */
-UA_Byte UA_FLOAT_ZERO[] = { 0x00, 0x00, 0x00, 0x00 };
-static UA_StatusCode
-Float_decodeBinary(bufpos pos, bufend end, UA_Float *dst) {
-    if(*pos + sizeof(UA_Float) > end)
-        return UA_STATUSCODE_BADDECODINGERROR;
-    UA_Float mantissa;
-    UA_UInt32 biasedExponent;
-    UA_Float sign;
-    if(memcmp(*pos, UA_FLOAT_ZERO, 4) == 0)
-        return Int32_decodeBinary(pos, end, (UA_Int32*) dst);
-    mantissa = (UA_Float)(**pos & 0xFF); // bits 0-7
-    mantissa = (mantissa / 256.0) + (UA_Float)((*pos)[1] & 0xFF); // bits 8-15
-    mantissa = (mantissa / 256.0) + (UA_Float)((*pos)[2] & 0x7F); // bits 16-22
-    biasedExponent = ((*pos)[2] & 0x80) >> 7; // bits 23
-    biasedExponent |= ((*pos)[3] & 0x7F) << 1; // bits 24-30
-    sign = ((*pos)[3] & 0x80) ? -1.0 : 1.0; // bit 31
-    if(biasedExponent >= 127)
-        *dst = (UA_Float)sign*(1<<(biasedExponent-127))*(1.0+mantissa/128.0);
-    else
-        *dst = (UA_Float)sign*2.0*(1.0+mantissa/128.0)/((UA_Float)(biasedExponent-127));
-    *offset += 4;
-    return UA_STATUSCODE_GOOD;
+#define FLOAT_NAN 0x7fc00000
+#define FLOAT_INF 0x7f800000
+#define FLOAT_NEG_INF 0xff800000
+#define FLOAT_NEG_ZERO 0x80000000
+
+static UA_UInt32 float_to_ieee754(UA_Float f) {
+    if(f != f) return FLOAT_NAN;
+    if(f == 0.0f) return signbit(f) ? FLOAT_NEG_ZERO : 0;
+    if(f/f != f/f) return f > 0 ? FLOAT_INF : FLOAT_NEG_INF;
+    UA_UInt32 sign = 0;
+    UA_Double fnorm = f;
+    if(f < 0) { sign = 1; fnorm = -f; }
+    UA_Int32 shift = 0;
+    while(fnorm >= 2.0) { fnorm /= 2.0; shift++; }
+    while(fnorm < 1.0) { fnorm *= 2.0; shift--; }
+    fnorm -= 1.0;
+    UA_Int64 significand = (long long)(fnorm * ((UA_Double)(1LL<<23) + 0.5f));
+    UA_Int64 exp = shift + ((1<<7) - 1);
+    return (UA_UInt32)((sign<<31) | (exp<<23) | significand);
 }
 
 static UA_StatusCode
 Float_encodeBinary(UA_Float const *src, bufpos pos, bufend end) {
-    if(*pos + sizeof(UA_Float) > end)
-        return UA_STATUSCODE_BADENCODINGERROR;
-    UA_Float srcFloat = *src;
-    **pos = (UA_Byte) (((UA_Int32) srcFloat & 0xFF000000) >> 24); (*pos)++;
-    **pos = (UA_Byte) (((UA_Int32) srcFloat & 0x00FF0000) >> 16); (*pos)++;
-    **pos = (UA_Byte) (((UA_Int32) srcFloat & 0x0000FF00) >> 8); (*pos)++;
-    **pos = (UA_Byte) ((UA_Int32)  srcFloat & 0x000000FF); (*pos)++;
+    UA_UInt32 encoded = float_to_ieee754(*src);
+    return UInt32_encodeBinary(&encoded, pos, end);
+}
+
+static UA_Float ieee754_to_float(UA_UInt32 i) {
+    if(i == 0) return 0.0;
+    if(i == FLOAT_NEG_ZERO) return -0.0;
+    if(i == FLOAT_INF) return INFINITY;
+    if(i == FLOAT_NEG_INF) return -INFINITY;
+    if(i == FLOAT_NAN) return NAN;
+    UA_Float result = (UA_Float)(i&((1LL<<23)-1));
+    result /= (1LL<<23);
+    result += 1.0f;
+    unsigned bias = (1<<7) - 1;
+    long long shift = ((i>>23)&((1LL<<8)-1)) - bias;
+    while(shift > 0) { result *= 2.0f; shift--; }
+    while(shift < 0) { result /= 2.0f; shift++; }
+    result *= (i>>31) & 1 ? -1.0f : 1.0f;
+    return result;
+}
+
+static UA_StatusCode Float_decodeBinary(bufpos pos, bufend end, UA_Float *dst) {
+    UA_UInt32 decoded;
+    UA_StatusCode retval = UInt32_decodeBinary(pos, end, &decoded);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    *dst = ieee754_to_float(decoded);
     return UA_STATUSCODE_GOOD;
 }
 

+ 28 - 17
tests/check_builtin.c

@@ -1,10 +1,11 @@
 #include <stdio.h>
 #include <stdlib.h>
+#include <math.h>
+#include <float.h>
 #include "ua_types.h"
 #include "ua_types_encoding_binary.h"
 #include "ua_types_generated.h"
 #include "ua_types_generated_encoding_binary.h"
-//#include "ua_transport.h"
 #include "ua_util.h"
 #include "check.h"
 
@@ -810,23 +811,33 @@ START_TEST(UA_Int64_encodeNegativeShallEncodeLittleEndian) {
 END_TEST
 
 START_TEST(UA_Float_encodeShallWorkOnExample) {
-    // given
-    UA_Float src;
-    UA_Byte  data[]   = {  0x55, 0x55,    0x55,  0x55,    0x55,    0x55,  0x55,       0x55,
-            0x55,  0x55,    0x55,  0x55,    0x55,    0x55,  0x55,       0x55 };
-    UA_ByteString dst = { 16, data };
-
-    UA_Int32  retval  = 0;
-    size_t pos     = 0;
+#define UA_FLOAT_TESTS 9
+    UA_Float src[UA_FLOAT_TESTS] = {27.5f, -6.5f, 0.0f, -0.0f, NAN, FLT_MAX, FLT_MIN, INFINITY, -INFINITY};
+    UA_Byte result[UA_FLOAT_TESTS][4] = {
+        {0x00, 0x00, 0xDC, 0x41}, // 27.5
+        {0x00, 0x00, 0xD0, 0xC0}, // -6.5
+        {0x00, 0x00, 0x00, 0x00}, // 0.0
+        {0x00, 0x00, 0x00, 0x80}, // -0.0
+        {0x00, 0x00, 0xC0, 0x7F}, // NAN
+        {0xFF, 0xFF, 0x7F, 0x7F}, // FLT_MAX
+        {0x00, 0x00, 0x80, 0x00}, // FLT_MIN
+        {0x00, 0x00, 0x80, 0x7F}, // INF
+        {0x00, 0x00, 0x80, 0xFF} // -INF
+    };
 
-    // when test 1
-    src    = -6.5;
-    retval = UA_Float_encodeBinary(&src, &dst, &pos);
-    // then test 1
-    ck_assert_int_eq(pos, 4);
-    ck_assert_int_eq(dst.data[2], 0xD0);
-    ck_assert_int_eq(dst.data[3], 0xC0);
-    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+    UA_Byte data[] = {0x55, 0x55, 0x55,  0x55};
+    UA_ByteString dst = {4, data};
+
+    for(size_t i = 0; i < UA_FLOAT_TESTS; i++) {
+        size_t pos = 0;
+        UA_Int32 retval = UA_Float_encodeBinary(&src[i], &dst, &pos);
+        ck_assert_int_eq(pos, 4);
+        ck_assert_int_eq(dst.data[0], result[i][0]);
+        ck_assert_int_eq(dst.data[1], result[i][1]);
+        ck_assert_int_eq(dst.data[2], result[i][2]);
+        ck_assert_int_eq(dst.data[3], result[i][3]);
+        ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+    }
 }
 END_TEST