wireshark/epan/ftypes/ftype-ieee-11073-float.c

1520 lines
40 KiB
C

/* FLOATs as specified by ISO/IEEE Std. 11073-20601-2014
*
* Personal Health Devices Transcoding White Paper v1.5
* https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=272346
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 2001 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config.h"
#include <stdio.h>
#include <ftypes-int.h>
#include <math.h>
#include <errno.h>
#include <float.h>
#include "strutil.h"
#define DOUBLE_REPR_LENGTH 27
#define SFLOAT_VALUE_INFINITY_PLUS 0x07FE
#define SFLOAT_VALUE_NAN 0x07FF
#define SFLOAT_VALUE_NRES 0x0800
#define SFLOAT_VALUE_RFU 0x0801
#define SFLOAT_VALUE_INFINITY_MINUS 0x0802
#define FLOAT_VALUE_INFINITY_PLUS 0x007FFFFE
#define FLOAT_VALUE_NAN 0x007FFFFF
#define FLOAT_VALUE_NRES 0x00800000
#define FLOAT_VALUE_RFU 0x00800001
#define FLOAT_VALUE_INFINITY_MINUS 0x00800002
static void
sfloat_ieee_11073_fvalue_new(fvalue_t *fv)
{
fv->value.sfloat_ieee_11073 = 0x0000;
}
static gboolean
sfloat_ieee_11073_val_from_unparsed(fvalue_t *fv, const char *s, gboolean allow_partial_value _U_, gchar **err_msg _U_)
{
const char *i_char = s;
char c;
guint8 mantissa_sign = 0;
guint32 mantissa = 0;
gint8 exponent = 0;
gboolean fraction_mode = FALSE;
const guint16 mantissa_max = 0x07FF;
c = *i_char;
if (c== '\0')
return FALSE;
if (c == '.')
return FALSE;
if (c == '-' && s[1] == '.')
return FALSE;
if (c == '-' && (s[1] == 'I' || s[1] == 'i')) {
if (!g_ascii_strcasecmp(s, "-INFINITY")) {
fv->value.sfloat_ieee_11073 = SFLOAT_VALUE_INFINITY_MINUS;
return TRUE;
}
return FALSE;
} else if (c == 'R' || c == 'r') {
if (!g_ascii_strcasecmp(s, "RFU")) {
fv->value.sfloat_ieee_11073 = SFLOAT_VALUE_RFU;
return TRUE;
}
return FALSE;
} else if (c == 'N' || c == 'n') {
if (!g_ascii_strcasecmp(s, "NRes")) {
fv->value.sfloat_ieee_11073 = SFLOAT_VALUE_NRES;
return TRUE;
}
if (!g_ascii_strcasecmp(s, "NaN")) {
fv->value.sfloat_ieee_11073 = SFLOAT_VALUE_NAN;
return TRUE;
}
return FALSE;
} else if (c == '+') {
if (!g_ascii_strcasecmp(s, "+INFINITY")) {
fv->value.sfloat_ieee_11073 = SFLOAT_VALUE_INFINITY_PLUS;
return TRUE;
}
return FALSE;
}
if (c == '-') {
if (s[1] == '\0')
return FALSE;
mantissa_sign = 1;
i_char += 1;
}
while (*i_char == '0') {
i_char += 1;
}
c = *i_char;
do {
if (c == '0') {
if (mantissa * 10 > (guint32) mantissa_max + mantissa_sign) {
exponent += 1;
if (exponent > 7)
return FALSE;
} else {
mantissa *= 10;
}
} else if (c == '1') {
mantissa *= 10;
mantissa += 1;
} else if (c == '2') {
mantissa *= 10;
mantissa += 2;
} else if (c == '3') {
mantissa *= 10;
mantissa += 3;
} else if (c == '4') {
mantissa *= 10;
mantissa += 4;
} else if (c == '5') {
mantissa *= 10;
mantissa += 5;
} else if (c == '6') {
mantissa *= 10;
mantissa += 6;
} else if (c == '7') {
mantissa *= 10;
mantissa += 7;
} else if (c == '8') {
mantissa *= 10;
mantissa += 8;
} else if (c == '9') {
mantissa *= 10;
mantissa += 9;
} else if (c == '.') {
if (fraction_mode)
return FALSE;
fraction_mode = TRUE;
i_char += 1;
while (*i_char == '0') {
i_char += 1;
if (mantissa * 10 <= (guint32) mantissa_max + mantissa_sign) {
mantissa *= 10;
if (exponent > -8 - 4) /* -8 is min exponent; 4 is mantissa size */
exponent -= 1;
}
}
i_char -= 1;
} else if (c != '\0') {
/* NOTE: Maybe 5e-10, 5e3 notation should be also supported */
return FALSE;
}
if (mantissa > (guint32) mantissa_max + mantissa_sign)
return FALSE;
if (c != '.' && fraction_mode)
exponent -= 1;
i_char += 1;
} while ((c = *i_char));
if (mantissa_sign) {
mantissa = ~(mantissa - 1);
mantissa &= 0x0FFF;
}
/* Transform to normal form */
if (mantissa == 0)
exponent = 0;
while (mantissa > 0 && mantissa % 10 == 0 && exponent < 7) {
mantissa /= 10;
exponent += 1;
}
if (exponent < -8)
return FALSE;
fv->value.sfloat_ieee_11073 = ((exponent & 0x0F) << 12) | mantissa;
return TRUE;
}
static void
sfloat_ieee_11073_val_to_repr(fvalue_t *fv, ftrepr_t rtype _U_, int field_display _U_, char *buf, unsigned int size)
{
gint8 exponent;
guint16 mantissa;
guint16 mantissa_sign;
guint32 offset = 0;
#define MANTISSA_STR_BUFFER_SIZE 5
gchar mantissa_str[MANTISSA_STR_BUFFER_SIZE];
guint8 mantissa_digits;
if (fv->value.sfloat_ieee_11073 >= 0x07FE && fv->value.sfloat_ieee_11073 <= 0x0802) {
switch (fv->value.sfloat_ieee_11073) {
case SFLOAT_VALUE_INFINITY_PLUS:
(void) g_strlcpy(buf, "+INFINITY", size);
break;
case SFLOAT_VALUE_NAN:
(void) g_strlcpy(buf, "NaN", size);
break;
case SFLOAT_VALUE_NRES:
(void) g_strlcpy(buf, "NRes", size);
break;
case SFLOAT_VALUE_RFU:
(void) g_strlcpy(buf, "RFU", size);
break;
case SFLOAT_VALUE_INFINITY_MINUS:
(void) g_strlcpy(buf, "-INFINITY", size);
break;
}
return;
}
exponent = fv->value.sfloat_ieee_11073 >> 12;
if (exponent & 0x8)
exponent |= 0xF0; /* It is signed (4bits), so make it signed in gint8 */
mantissa = fv->value.sfloat_ieee_11073 & 0x07FF;
mantissa_sign = (fv->value.sfloat_ieee_11073 & 0x0800);
if (mantissa_sign)
mantissa = -((gint16)mantissa | 0xF800);
if (mantissa == 0) {
buf[0] = '0';
buf[1] = '\0';
return;
}
if (mantissa_sign) {
buf[0] = '-';
offset += 1;
}
mantissa_digits = g_snprintf(mantissa_str, MANTISSA_STR_BUFFER_SIZE, "%u", mantissa);
if (exponent == 0) {
memcpy(buf + offset, mantissa_str, mantissa_digits);
offset += mantissa_digits;
} else if (exponent > 0) {
memcpy(buf + offset, mantissa_str, mantissa_digits);
offset += mantissa_digits;
memset(buf + offset, '0', exponent);
offset += exponent;
} else /* if (exponent < 0)*/ {
if (-exponent < mantissa_digits) {
memcpy(buf + offset, mantissa_str, mantissa_digits + exponent);
offset += mantissa_digits + exponent;
buf[offset] = '.';
offset += 1;
memcpy(buf + offset, mantissa_str + mantissa_digits + exponent, -exponent);
offset += -exponent;
} else {
buf[offset] = '0';
offset += 1;
buf[offset] = '.';
offset += 1;
if (-exponent - mantissa_digits > 0) {
memset(buf + offset, '0', -exponent - mantissa_digits);
offset += -exponent - mantissa_digits;
}
memcpy(buf + offset, mantissa_str, mantissa_digits);
offset += mantissa_digits;
}
}
buf[offset] = '\0';
}
static int
sfloat_ieee_11073_val_repr_len(fvalue_t *fv _U_, ftrepr_t rtype _U_, int field_display _U_)
{
/* Predefinied: +INFINITY, -INFINITY, RFU, NRes, NaN */
/* Longest Signed Float Number: -0.00002048 (11 characters without NULL) */
/* Longest Signed Float Number -0.00000001 */
/* Longest Signed Nonfloat Number: -20480000000 (12 characters without NULL) */
/* NOTE: Possible memory optimization: compute length, but watch out for speed */
return 13;
}
static void
sfloat_ieee_11073_value_set(fvalue_t *fv, guint32 value)
{
fv->value.sfloat_ieee_11073 = (guint16) value;
}
static guint32
sfloat_ieee_11073_value_get(fvalue_t *fv)
{
return (guint32) fv->value.sfloat_ieee_11073;
}
static guint16 sfloat_to_normal_form(guint16 value)
{
gint8 exponent;
guint16 mantissa;
guint8 mantissa_sign;
if (value >= 0x07FE && value <= 0x0802) /* Save special values */
return value;
mantissa = value & 0x07FF;
if (value & 0x0800) {
mantissa = -((gint16)mantissa | 0xF800);
mantissa_sign = 1;
} else {
mantissa_sign = 0;
}
exponent = value >> 12;
if (exponent & 0x08) {
exponent |= 0xF0;
}
while ((!(mantissa % 10)) && mantissa != 0) {
mantissa /= 10;
if (exponent == 7)
break;
exponent += 1;
}
return ((((exponent & 0x80) ? 0x8 : 0x0 ) | (exponent & 0x7)) << 12) | (mantissa_sign << 11) | mantissa;
}
static gboolean
sfloat_ieee_11073_cmp_eq(const fvalue_t *a, const fvalue_t *b)
{
return sfloat_to_normal_form(a->value.sfloat_ieee_11073) == sfloat_to_normal_form(b->value.sfloat_ieee_11073);
}
static gboolean
sfloat_ieee_11073_cmp_ne(const fvalue_t *a, const fvalue_t *b)
{
return sfloat_to_normal_form(a->value.sfloat_ieee_11073) != sfloat_to_normal_form(b->value.sfloat_ieee_11073);
}
static gboolean
sfloat_ieee_11073_cmp_gt(const fvalue_t *a, const fvalue_t *b)
{
guint16 a_norm;
guint16 b_norm;
gint16 a_norm_mantissa;
gint16 b_norm_mantissa;
gint8 a_norm_exponent;
gint8 b_norm_exponent;
a_norm = sfloat_to_normal_form(a->value.sfloat_ieee_11073);
b_norm = sfloat_to_normal_form(b->value.sfloat_ieee_11073);
if (a_norm == b_norm)
return FALSE;
switch (a_norm) {
case SFLOAT_VALUE_NAN:
case SFLOAT_VALUE_NRES:
case SFLOAT_VALUE_RFU:
case SFLOAT_VALUE_INFINITY_MINUS:
return FALSE;
case SFLOAT_VALUE_INFINITY_PLUS:
switch (b_norm) {
case SFLOAT_VALUE_NAN:
case SFLOAT_VALUE_NRES:
case SFLOAT_VALUE_RFU:
case SFLOAT_VALUE_INFINITY_PLUS: /* Dead, informative case */
return FALSE;
case SFLOAT_VALUE_INFINITY_MINUS:
default:
return TRUE;
}
}
a_norm_mantissa = a_norm & 0x0FFF;
b_norm_mantissa = b_norm & 0x0FFF;
if (a_norm & 0x0800)
a_norm_mantissa |= 0xF000;
if (b_norm & 0x0800)
b_norm_mantissa |= 0xF000;
a_norm_exponent = a_norm >> 12;
b_norm_exponent = b_norm >> 12;
if (a_norm_exponent & 0x08) {
a_norm_exponent |= 0xF0;
}
if (b_norm_exponent & 0x08) {
b_norm_exponent |= 0xF0;
}
if (a_norm_mantissa == b_norm_mantissa && a_norm_exponent > b_norm_exponent)
return TRUE;
if (a_norm_exponent == b_norm_exponent && a_norm_mantissa > b_norm_mantissa)
return TRUE;
if (a_norm_exponent < b_norm_exponent) {
guint8 exponent_difference;
exponent_difference = b_norm_exponent - a_norm_exponent;
if (exponent_difference >= 4)
return FALSE;
while (exponent_difference--) {
b_norm_mantissa *= 10;
}
} else {
guint8 exponent_difference;
exponent_difference = a_norm_exponent - b_norm_exponent;
if (exponent_difference >= 4)
return TRUE;
while (exponent_difference--) {
a_norm_mantissa *= 10;
}
}
if (a_norm_mantissa > b_norm_mantissa)
return TRUE;
return FALSE;
}
static gboolean
sfloat_ieee_11073_cmp_ge(const fvalue_t *a, const fvalue_t *b)
{
guint16 a_norm;
guint16 b_norm;
gint16 a_norm_mantissa;
gint16 b_norm_mantissa;
gint8 a_norm_exponent;
gint8 b_norm_exponent;
a_norm = sfloat_to_normal_form(a->value.sfloat_ieee_11073);
b_norm = sfloat_to_normal_form(b->value.sfloat_ieee_11073);
if (a_norm == b_norm)
return TRUE;
switch (a_norm) {
case SFLOAT_VALUE_NAN:
case SFLOAT_VALUE_NRES:
case SFLOAT_VALUE_RFU:
case SFLOAT_VALUE_INFINITY_MINUS:
return FALSE;
case SFLOAT_VALUE_INFINITY_PLUS:
switch (b_norm) {
case SFLOAT_VALUE_NAN:
case SFLOAT_VALUE_NRES:
case SFLOAT_VALUE_RFU:
case SFLOAT_VALUE_INFINITY_PLUS: /* Dead, informative case */
return FALSE;
case SFLOAT_VALUE_INFINITY_MINUS:
default:
return TRUE;
}
}
a_norm_mantissa = a_norm & 0x0FFF;
b_norm_mantissa = b_norm & 0x0FFF;
if (a_norm & 0x0800)
a_norm_mantissa |= 0xF000;
if (b_norm & 0x0800)
b_norm_mantissa |= 0xF000;
a_norm_exponent = a_norm >> 12;
b_norm_exponent = b_norm >> 12;
if (a_norm_exponent & 0x08) {
a_norm_exponent |= 0xF0;
}
if (b_norm_exponent & 0x08) {
b_norm_exponent |= 0xF0;
}
if (a_norm_mantissa == b_norm_mantissa && a_norm_exponent >= b_norm_exponent)
return TRUE;
if (a_norm_exponent == b_norm_exponent && a_norm_mantissa >= b_norm_mantissa)
return TRUE;
if (a_norm_exponent < b_norm_exponent) {
guint8 exponent_difference;
exponent_difference = b_norm_exponent - a_norm_exponent;
if (exponent_difference >= 4)
return FALSE;
while (exponent_difference--) {
b_norm_mantissa *= 10;
}
} else {
guint8 exponent_difference;
exponent_difference = a_norm_exponent - b_norm_exponent;
if (exponent_difference >= 4)
return TRUE;
while (exponent_difference--) {
a_norm_mantissa *= 10;
}
}
if (a_norm_mantissa > b_norm_mantissa)
return TRUE;
return FALSE;
}
static gboolean
sfloat_ieee_11073_cmp_lt(const fvalue_t *a, const fvalue_t *b)
{
guint16 a_norm;
guint16 b_norm;
gint16 a_norm_mantissa;
gint16 b_norm_mantissa;
gint8 a_norm_exponent;
gint8 b_norm_exponent;
a_norm = sfloat_to_normal_form(a->value.sfloat_ieee_11073);
b_norm = sfloat_to_normal_form(b->value.sfloat_ieee_11073);
if (a_norm == b_norm)
return FALSE;
switch (a_norm) {
case SFLOAT_VALUE_NAN:
case SFLOAT_VALUE_NRES:
case SFLOAT_VALUE_RFU:
case SFLOAT_VALUE_INFINITY_PLUS:
return FALSE;
case SFLOAT_VALUE_INFINITY_MINUS:
switch (b_norm) {
case SFLOAT_VALUE_NAN:
case SFLOAT_VALUE_NRES:
case SFLOAT_VALUE_RFU:
case SFLOAT_VALUE_INFINITY_MINUS: /* Dead, informative case */
return FALSE;
case SFLOAT_VALUE_INFINITY_PLUS:
default:
return TRUE;
}
}
a_norm_mantissa = a_norm & 0x0FFF;
b_norm_mantissa = b_norm & 0x0FFF;
if (a_norm & 0x0800)
a_norm_mantissa |= 0xFFFFF000;
if (b_norm & 0x0800)
b_norm_mantissa |= 0xFFFFF000;
a_norm_exponent = a_norm >> 12;
b_norm_exponent = b_norm >> 12;
if (a_norm_exponent & 0x08) {
a_norm_exponent |= 0xF0;
}
if (b_norm_exponent & 0x08) {
b_norm_exponent |= 0xF0;
}
if (a_norm_mantissa == b_norm_mantissa && a_norm_exponent < b_norm_exponent)
return TRUE;
if (a_norm_exponent == b_norm_exponent && a_norm_mantissa < b_norm_mantissa)
return TRUE;
if (a_norm_exponent < b_norm_exponent) {
guint8 exponent_difference;
exponent_difference = b_norm_exponent - a_norm_exponent;
if (exponent_difference >= 4)
return TRUE;
while (exponent_difference--) {
b_norm_mantissa *= 10;
}
} else {
guint8 exponent_difference;
exponent_difference = a_norm_exponent - b_norm_exponent;
if (exponent_difference >= 4)
return FALSE;
while (exponent_difference--) {
a_norm_mantissa *= 10;
}
}
if (a_norm_mantissa < b_norm_mantissa)
return TRUE;
return FALSE;
}
static gboolean
sfloat_ieee_11073_cmp_le(const fvalue_t *a, const fvalue_t *b)
{
guint16 a_norm;
guint16 b_norm;
gint16 a_norm_mantissa;
gint16 b_norm_mantissa;
gint8 a_norm_exponent;
gint8 b_norm_exponent;
a_norm = sfloat_to_normal_form(a->value.sfloat_ieee_11073);
b_norm = sfloat_to_normal_form(b->value.sfloat_ieee_11073);
if (a_norm == b_norm)
return TRUE;
switch (a_norm) {
case SFLOAT_VALUE_NAN:
case SFLOAT_VALUE_NRES:
case SFLOAT_VALUE_RFU:
case SFLOAT_VALUE_INFINITY_PLUS:
return FALSE;
case SFLOAT_VALUE_INFINITY_MINUS:
switch (b_norm) {
case SFLOAT_VALUE_NAN:
case SFLOAT_VALUE_NRES:
case SFLOAT_VALUE_RFU:
case SFLOAT_VALUE_INFINITY_MINUS: /* Dead, informative case */
return FALSE;
case SFLOAT_VALUE_INFINITY_PLUS:
default:
return TRUE;
}
}
a_norm_mantissa = a_norm & 0x0FFF;
b_norm_mantissa = b_norm & 0x0FFF;
if (a_norm & 0x0800)
a_norm_mantissa |= 0xF000;
if (b_norm & 0x0800)
b_norm_mantissa |= 0xF000;
a_norm_exponent = a_norm >> 12;
b_norm_exponent = b_norm >> 12;
if (a_norm_exponent & 0x08) {
a_norm_exponent |= 0xF0;
}
if (b_norm_exponent & 0x08) {
b_norm_exponent |= 0xF0;
}
if (a_norm_mantissa == b_norm_mantissa && a_norm_exponent <= b_norm_exponent)
return TRUE;
if (a_norm_exponent == b_norm_exponent && a_norm_mantissa <= b_norm_mantissa)
return TRUE;
if (a_norm_exponent < b_norm_exponent) {
guint8 exponent_difference;
exponent_difference = b_norm_exponent - a_norm_exponent;
if (exponent_difference >= 4)
return TRUE;
while (exponent_difference--) {
b_norm_mantissa *= 10;
}
} else {
guint8 exponent_difference;
exponent_difference = a_norm_exponent - b_norm_exponent;
if (exponent_difference >= 4)
return FALSE;
while (exponent_difference--) {
a_norm_mantissa *= 10;
}
}
if (a_norm_mantissa < b_norm_mantissa)
return TRUE;
return FALSE;
}
static gboolean
sfloat_ieee_11073_cmp_bitwise_and(const fvalue_t *a, const fvalue_t *b)
{
return ((a->value.uinteger & b->value.uinteger) != 0);
}
/*============================================================================*/
static void
float_ieee_11073_fvalue_new(fvalue_t *fv)
{
fv->value.float_ieee_11073 = 0x0000;
}
static gboolean
float_ieee_11073_val_from_unparsed(fvalue_t *fv, const char *s, gboolean allow_partial_value _U_, gchar **err_msg _U_)
{
const char *i_char = s;
char c;
guint8 mantissa_sign = 0;
guint32 mantissa = 0;
gint16 exponent = 0;
gboolean fraction_mode = FALSE;
const guint32 mantissa_max = 0x007FFFFF;
c = *i_char;
if (c== '\0')
return FALSE;
if (c == '.')
return FALSE;
if (c == '-' && s[1] == '.')
return FALSE;
if (c == '-' && (s[1] == 'I' || s[1] == 'i')) {
if (!g_ascii_strcasecmp(s, "-INFINITY")) {
fv->value.float_ieee_11073 = FLOAT_VALUE_INFINITY_MINUS;
return TRUE;
}
return FALSE;
} else if (c == 'R' || c == 'r') {
if (!g_ascii_strcasecmp(s, "RFU")) {
fv->value.float_ieee_11073 = FLOAT_VALUE_RFU;
return TRUE;
}
return FALSE;
} else if (c == 'N' || c == 'n') {
if (!g_ascii_strcasecmp(s, "NRes")) {
fv->value.float_ieee_11073 = FLOAT_VALUE_NRES;
return TRUE;
}
if (!g_ascii_strcasecmp(s, "NaN")) {
fv->value.float_ieee_11073 = FLOAT_VALUE_NAN;
return TRUE;
}
return FALSE;
} else if (c == '+') {
if (!g_ascii_strcasecmp(s, "+INFINITY")) {
fv->value.float_ieee_11073 = FLOAT_VALUE_INFINITY_PLUS;
return TRUE;
}
return FALSE;
}
if (c == '-') {
if (s[1] == '\0')
return FALSE;
mantissa_sign = 1;
i_char += 1;
}
while (*i_char == '0') {
i_char += 1;
}
c = *i_char;
do {
if (c == '0') {
if (mantissa * 10 > mantissa_sign + mantissa_max) {
exponent += 1;
if (exponent <= 127)
return FALSE;
} else {
mantissa *= 10;
}
} else if (c == '1') {
mantissa *= 10;
mantissa += 1;
} else if (c == '2') {
mantissa *= 10;
mantissa += 2;
} else if (c == '3') {
mantissa *= 10;
mantissa += 3;
} else if (c == '4') {
mantissa *= 10;
mantissa += 4;
} else if (c == '5') {
mantissa *= 10;
mantissa += 5;
} else if (c == '6') {
mantissa *= 10;
mantissa += 6;
} else if (c == '7') {
mantissa *= 10;
mantissa += 7;
} else if (c == '8') {
mantissa *= 10;
mantissa += 8;
} else if (c == '9') {
mantissa *= 10;
mantissa += 9;
} else if (c == '.') {
if (fraction_mode)
return FALSE;
fraction_mode = TRUE;
i_char += 1;
while (*i_char == '0') {
i_char += 1;
if (mantissa * 10 <= mantissa_max + mantissa_sign) {
mantissa *= 10;
if (exponent > -128 - 7) /* -8 is min exponent; 4 is mantissa size */
exponent -= 1;
}
}
i_char -= 1;
} else if (c != '\0') {
/* NOTE: Maybe 5e-10, 5e3 notation should be also supported */
return FALSE;
}
if (mantissa > mantissa_max + mantissa_sign)
return FALSE;
if (c != '.' && fraction_mode)
exponent -= 1;
i_char += 1;
} while ((c = *i_char));
if (mantissa_sign) {
mantissa = ~(mantissa - 1);
mantissa &= 0x00FFFFFF;
}
/* Transform to normal form */
if (mantissa == 0)
exponent = 0;
while (mantissa > 0 && mantissa % 10 == 0 && exponent < 127) {
mantissa /= 10;
exponent += 1;
}
if (exponent < -128)
return FALSE;
fv->value.float_ieee_11073 = ((exponent & 0xFF) << 24) | mantissa;
return TRUE;
}
static void
float_ieee_11073_val_to_repr(fvalue_t *fv, ftrepr_t rtype _U_, int field_display _U_, char *buf, unsigned int size)
{
gint8 exponent;
guint32 mantissa;
guint32 mantissa_sign;
guint32 offset = 0;
gchar mantissa_str[8];
guint8 mantissa_digits;
if (fv->value.float_ieee_11073 >= 0x007FFFFE && fv->value.float_ieee_11073 <= 0x00800002) {
switch (fv->value.float_ieee_11073) {
case FLOAT_VALUE_INFINITY_PLUS:
(void) g_strlcpy(buf, "+INFINITY", size);
break;
case FLOAT_VALUE_NAN:
(void) g_strlcpy(buf, "NaN", size);
break;
case FLOAT_VALUE_NRES:
(void) g_strlcpy(buf, "NRes", size);
break;
case FLOAT_VALUE_RFU:
(void) g_strlcpy(buf, "RFU", size);
break;
case FLOAT_VALUE_INFINITY_MINUS:
(void) g_strlcpy(buf, "-INFINITY", size);
break;
}
}
exponent = fv->value.float_ieee_11073 >> 24;
mantissa = fv->value.float_ieee_11073 & 0x007FFFFF;
mantissa_sign = (fv->value.float_ieee_11073 & 0x00800000);
if (mantissa_sign)
mantissa = (guint32)(-((gint32)(mantissa | 0xFF000000)));
if (mantissa == 0) {
buf[0] = '0';
buf[1] = '\0';
return;
}
if (mantissa_sign) {
buf[0] = '-';
offset += 1;
}
mantissa_digits = g_snprintf(mantissa_str, size, "%u", mantissa);
if (exponent == 0) {
memcpy(buf + offset, mantissa_str, mantissa_digits);
offset += mantissa_digits;
} else if (exponent > 0) {
memcpy(buf + offset, mantissa_str, mantissa_digits);
offset += mantissa_digits;
memset(buf + offset, '0', exponent);
offset += exponent;
} else /* if (exponent < 0)*/ {
if (-exponent < mantissa_digits) {
memcpy(buf + offset, mantissa_str, mantissa_digits + exponent);
offset += mantissa_digits + exponent;
buf[offset] = '.';
offset += 1;
memcpy(buf + offset, mantissa_str + mantissa_digits + exponent, -exponent);
offset += -exponent;
} else {
buf[offset] = '0';
offset += 1;
buf[offset] = '.';
offset += 1;
if (-exponent - mantissa_digits > 0) {
memset(buf + offset, '0', -exponent - mantissa_digits);
offset += -exponent - mantissa_digits;
}
memcpy(buf + offset, mantissa_str, mantissa_digits);
offset += mantissa_digits;
}
}
buf[offset] = '\0';
}
static int
float_ieee_11073_val_repr_len(fvalue_t *fv _U_, ftrepr_t rtype _U_, int field_display _U_)
{
/* Predefinied: +INFINITY, -INFINITY, RFU, NRes, NaN */
/* Longest Signed Nonfloat Number: -8388608*(10^-128) (1 character for sign, 7 for mantisa digits, 127 zeros, 1 character for NULL) */
/* NOTE: Possible memory optimization: compute length, but watch out for speed */
return 136;
}
static void
float_ieee_11073_value_set(fvalue_t *fv, guint32 value)
{
fv->value.float_ieee_11073 = value;
}
static guint32
float_ieee_11073_value_get(fvalue_t *fv)
{
return fv->value.float_ieee_11073;
}
static guint32 float_to_normal_form(guint32 value)
{
gint8 exponent;
guint16 mantissa;
guint8 mantissa_sign;
if (value >= 0x007FFFFE && value <= 0x00800002) /* Save special values */
return value;
mantissa = value & 0x907FFFFF;
if (value & 0x00800000) {
mantissa = (guint32)(-((gint32)(mantissa | 0xFF000000)));
mantissa_sign = 1;
} else {
mantissa_sign = 0;
}
exponent = value >> 24;
while ((!(mantissa % 10)) && mantissa != 0) {
mantissa /= 10;
if (exponent == 127)
break;
exponent += 1;
}
return (exponent << 24) | (mantissa_sign << 23) | mantissa;
}
static gboolean
float_ieee_11073_cmp_eq(const fvalue_t *a, const fvalue_t *b)
{
return float_to_normal_form(a->value.float_ieee_11073) == float_to_normal_form(b->value.float_ieee_11073);
}
static gboolean
float_ieee_11073_cmp_ne(const fvalue_t *a, const fvalue_t *b)
{
return float_to_normal_form(a->value.float_ieee_11073) != float_to_normal_form(b->value.float_ieee_11073);
}
static gboolean
float_ieee_11073_cmp_gt(const fvalue_t *a, const fvalue_t *b)
{
guint32 a_norm;
guint32 b_norm;
gint32 a_norm_mantissa;
gint32 b_norm_mantissa;
gint8 a_norm_exponent;
gint8 b_norm_exponent;
a_norm = float_to_normal_form(a->value.float_ieee_11073);
b_norm = float_to_normal_form(b->value.float_ieee_11073);
if (a_norm == b_norm)
return FALSE;
switch (a_norm) {
case FLOAT_VALUE_NAN:
case FLOAT_VALUE_NRES:
case FLOAT_VALUE_RFU:
case FLOAT_VALUE_INFINITY_MINUS:
return FALSE;
case FLOAT_VALUE_INFINITY_PLUS:
switch (b_norm) {
case FLOAT_VALUE_NAN:
case FLOAT_VALUE_NRES:
case FLOAT_VALUE_RFU:
case FLOAT_VALUE_INFINITY_PLUS: /* Dead, informative case */
return FALSE;
case FLOAT_VALUE_INFINITY_MINUS:
default:
return TRUE;
}
}
a_norm_mantissa = a_norm & 0x00FFFFFF;
b_norm_mantissa = b_norm & 0x00FFFFFF;
if (a_norm & 0x00800000)
a_norm_mantissa |= 0xFF000000;
if (b_norm & 0x00800000)
b_norm_mantissa |= 0xFF000000;
a_norm_exponent = a_norm >> 24;
b_norm_exponent = b_norm >> 24;
if (a_norm_mantissa == b_norm_mantissa && a_norm_exponent > b_norm_exponent)
return TRUE;
if (a_norm_exponent == b_norm_exponent && a_norm_mantissa > b_norm_mantissa)
return TRUE;
if (a_norm_exponent < b_norm_exponent) {
guint8 exponent_difference;
exponent_difference = b_norm_exponent - a_norm_exponent;
if (exponent_difference >= 7)
return FALSE;
while (exponent_difference--) {
b_norm_mantissa *= 10;
}
} else {
guint8 exponent_difference;
exponent_difference = a_norm_exponent - b_norm_exponent;
if (exponent_difference >= 7)
return TRUE;
while (exponent_difference--) {
a_norm_mantissa *= 10;
}
}
if (a_norm_mantissa > b_norm_mantissa)
return TRUE;
return FALSE;
}
static gboolean
float_ieee_11073_cmp_ge(const fvalue_t *a, const fvalue_t *b)
{
guint32 a_norm;
guint32 b_norm;
gint32 a_norm_mantissa;
gint32 b_norm_mantissa;
gint8 a_norm_exponent;
gint8 b_norm_exponent;
a_norm = float_to_normal_form(a->value.float_ieee_11073);
b_norm = float_to_normal_form(b->value.float_ieee_11073);
if (a_norm == b_norm)
return TRUE;
switch (a_norm) {
case FLOAT_VALUE_NAN:
case FLOAT_VALUE_NRES:
case FLOAT_VALUE_RFU:
case FLOAT_VALUE_INFINITY_MINUS:
return FALSE;
case FLOAT_VALUE_INFINITY_PLUS:
switch (b_norm) {
case FLOAT_VALUE_NAN:
case FLOAT_VALUE_NRES:
case FLOAT_VALUE_RFU:
case FLOAT_VALUE_INFINITY_PLUS: /* Dead, informative case */
return FALSE;
case FLOAT_VALUE_INFINITY_MINUS:
default:
return TRUE;
}
}
a_norm_mantissa = a_norm & 0x00FFFFFF;
b_norm_mantissa = b_norm & 0x00FFFFFF;
if (a_norm & 0x00800000)
a_norm_mantissa |= 0xFF000000;
if (b_norm & 0x00800000)
b_norm_mantissa |= 0xFF000000;
a_norm_exponent = a_norm >> 24;
b_norm_exponent = b_norm >> 24;
if (a_norm_mantissa == b_norm_mantissa && a_norm_exponent >= b_norm_exponent)
return TRUE;
if (a_norm_exponent == b_norm_exponent && a_norm_mantissa >= b_norm_mantissa)
return TRUE;
if (a_norm_exponent < b_norm_exponent) {
guint8 exponent_difference;
exponent_difference = b_norm_exponent - a_norm_exponent;
if (exponent_difference >= 7)
return FALSE;
while (exponent_difference--) {
b_norm_mantissa *= 10;
}
} else {
guint8 exponent_difference;
exponent_difference = a_norm_exponent - b_norm_exponent;
if (exponent_difference >= 7)
return TRUE;
while (exponent_difference--) {
a_norm_mantissa *= 10;
}
}
if (a_norm_mantissa > b_norm_mantissa)
return TRUE;
return FALSE;
}
static gboolean
float_ieee_11073_cmp_lt(const fvalue_t *a, const fvalue_t *b)
{
guint32 a_norm;
guint32 b_norm;
gint32 a_norm_mantissa;
gint32 b_norm_mantissa;
gint8 a_norm_exponent;
gint8 b_norm_exponent;
a_norm = float_to_normal_form(a->value.float_ieee_11073);
b_norm = float_to_normal_form(b->value.float_ieee_11073);
if (a_norm == b_norm)
return FALSE;
switch (a_norm) {
case FLOAT_VALUE_NAN:
case FLOAT_VALUE_NRES:
case FLOAT_VALUE_RFU:
case FLOAT_VALUE_INFINITY_PLUS:
return FALSE;
case FLOAT_VALUE_INFINITY_MINUS:
switch (b_norm) {
case FLOAT_VALUE_NAN:
case FLOAT_VALUE_NRES:
case FLOAT_VALUE_RFU:
case FLOAT_VALUE_INFINITY_MINUS: /* Dead, informative case */
return FALSE;
case FLOAT_VALUE_INFINITY_PLUS:
default:
return TRUE;
}
}
a_norm_mantissa = a_norm & 0x00FFFFFF;
b_norm_mantissa = b_norm & 0x00FFFFFF;
if (a_norm & 0x00800000)
a_norm_mantissa |= 0xFF000000;
if (b_norm & 0x00800000)
b_norm_mantissa |= 0xFF000000;
a_norm_exponent = a_norm >> 24;
b_norm_exponent = b_norm >> 24;
if (a_norm_mantissa == b_norm_mantissa && a_norm_exponent < b_norm_exponent)
return TRUE;
if (a_norm_exponent == b_norm_exponent && a_norm_mantissa < b_norm_mantissa)
return TRUE;
if (a_norm_exponent < b_norm_exponent) {
guint8 exponent_difference;
exponent_difference = b_norm_exponent - a_norm_exponent;
if (exponent_difference >= 7)
return TRUE;
while (exponent_difference--) {
b_norm_mantissa *= 10;
}
} else {
guint8 exponent_difference;
exponent_difference = a_norm_exponent - b_norm_exponent;
if (exponent_difference >= 7)
return FALSE;
while (exponent_difference--) {
a_norm_mantissa *= 10;
}
}
if (a_norm_mantissa < b_norm_mantissa)
return TRUE;
return FALSE;
}
static gboolean
float_ieee_11073_cmp_le(const fvalue_t *a, const fvalue_t *b)
{
guint32 a_norm;
guint32 b_norm;
gint32 a_norm_mantissa;
gint32 b_norm_mantissa;
gint8 a_norm_exponent;
gint8 b_norm_exponent;
a_norm = float_to_normal_form(a->value.float_ieee_11073);
b_norm = float_to_normal_form(b->value.float_ieee_11073);
if (a_norm == b_norm)
return TRUE;
switch (a_norm) {
case FLOAT_VALUE_NAN:
case FLOAT_VALUE_NRES:
case FLOAT_VALUE_RFU:
case FLOAT_VALUE_INFINITY_PLUS:
return FALSE;
case FLOAT_VALUE_INFINITY_MINUS:
switch (b_norm) {
case FLOAT_VALUE_NAN:
case FLOAT_VALUE_NRES:
case FLOAT_VALUE_RFU:
case FLOAT_VALUE_INFINITY_MINUS: /* Dead, informative case */
return FALSE;
case FLOAT_VALUE_INFINITY_PLUS:
default:
return TRUE;
}
}
a_norm_mantissa = a_norm & 0x00FFFFFF;
b_norm_mantissa = b_norm & 0x00FFFFFF;
if (a_norm & 0x00800000)
a_norm_mantissa |= 0xFF000000;
if (b_norm & 0x00800000)
b_norm_mantissa |= 0xFF000000;
a_norm_exponent = a_norm >> 24;
b_norm_exponent = b_norm >> 24;
if (a_norm_mantissa == b_norm_mantissa && a_norm_exponent <= b_norm_exponent)
return TRUE;
if (a_norm_exponent == b_norm_exponent && a_norm_mantissa <= b_norm_mantissa)
return TRUE;
if (a_norm_exponent < b_norm_exponent) {
guint8 exponent_difference;
exponent_difference = b_norm_exponent - a_norm_exponent;
if (exponent_difference >= 7)
return TRUE;
while (exponent_difference--) {
b_norm_mantissa *= 10;
}
} else {
guint8 exponent_difference;
exponent_difference = a_norm_exponent - b_norm_exponent;
if (exponent_difference >= 7)
return FALSE;
while (exponent_difference--) {
a_norm_mantissa *= 10;
}
}
if (a_norm_mantissa < b_norm_mantissa)
return TRUE;
return FALSE;
}
static gboolean
float_ieee_11073_cmp_bitwise_and(const fvalue_t *a, const fvalue_t *b)
{
return ((a->value.uinteger & b->value.uinteger) != 0);
}
/*============================================================================*/
void
ftype_register_ieee_11073_float(void)
{
/*
Size: 16bits = 2 octets
Exponent: 4 bits (signed integer - 2's complement)
Mantissa: 12 bits (signed integer - 2's complement)
Base: 10
x = M * (10 ^ E)
Exponent range: from -8 to 7
Mantissa range: from -2048 to 2047 (4 digits)
Special values:
+ INFINITY [exponent 0, mantissa +(2^11 -2) = 0x07FE]
NaN (Not a Number) [exponent 0, mantissa +(2^11 -1) = 0x07FF]
NRes (Not at this Resolution) [exponent 0, mantissa -(2^11) = 0x0800]
Reserved for future use [exponent 0, mantissa -(2^11 -1) = 0x0801]
- INFINITY [exponent 0, mantissa -(2^11 -2) = 0x0802]
Note:
be carefour when comparing: 1e == 10e-1 == 10e-2 == ... (solution: compare only if the lowest mantissa % 10 != 0)
Example: 114 is 0x0072
*/
static ftype_t sfloat_type = {
FT_IEEE_11073_SFLOAT, /* ftype */
"FT_IEEE_11073_SFLOAT", /* name */
"IEEE-11073 Floating point (16-bit)", /* pretty_name */
2, /* wire_size */
sfloat_ieee_11073_fvalue_new, /* new_value */
NULL, /* free_value */
sfloat_ieee_11073_val_from_unparsed, /* val_from_unparsed */
NULL, /* val_from_string */
sfloat_ieee_11073_val_to_repr, /* val_to_string_repr */
sfloat_ieee_11073_val_repr_len, /* len_string_repr */
{ .set_value_uinteger = sfloat_ieee_11073_value_set }, /* union set_value */
{ .get_value_uinteger = sfloat_ieee_11073_value_get }, /* union get_value */
sfloat_ieee_11073_cmp_eq,
sfloat_ieee_11073_cmp_ne,
sfloat_ieee_11073_cmp_gt,
sfloat_ieee_11073_cmp_ge,
sfloat_ieee_11073_cmp_lt,
sfloat_ieee_11073_cmp_le,
sfloat_ieee_11073_cmp_bitwise_and, /* cmp_bitwise_and */
NULL, /* cmp_contains */
NULL, /* cmp_matches */
NULL, /* len */
NULL, /* slice */
};
/*
Size: 32bits = 4 octets
Exponent: 1 octet (signed integer - 2's complement)
Mantissa: 3 octets (signed integer - 2's complement)
Base: 10
x = M * (10 ^ E)
Exponent range: from -128 to 127
Mantissa range: from -8388608 to 8388607 (7 digits)
Special values:
+ INFINITY [exponent 0, mantissa +(2^23 -2) = 0x007FFFFE]
NaN (Not a Number) [exponent 0, mantissa +(2^23 -1) = 0x007FFFFF]
NRes (Not at this Resolution) [exponent 0, mantissa -(2^23) = 0x00800000]
Reserved for future use [exponent 0, mantissa -(2^23-1) = 0x00800001]
- INFINITY [exponent 0, mantissa -(2^23 -2) = 0x00800002]
Note:
be carefour when comparing: 1e == 10e-1 == 10e-2 == ... (solution: compare only if the lowest mantissa % 10 != 0)
Example: 36.4 is 0xFF00016C
*/
static ftype_t float_type = {
FT_IEEE_11073_FLOAT, /* ftype */
"FT_IEEE_11073_FLOAT", /* name */
"IEEE-11073 Floating point (32-bit)", /* pretty_name */
4, /* wire_size */
float_ieee_11073_fvalue_new, /* new_value */
NULL, /* free_value */
float_ieee_11073_val_from_unparsed, /* val_from_unparsed */
NULL, /* val_from_string */
float_ieee_11073_val_to_repr, /* val_to_string_repr */
float_ieee_11073_val_repr_len, /* len_string_repr */
{ .set_value_uinteger = float_ieee_11073_value_set }, /* union set_value */
{ .get_value_uinteger = float_ieee_11073_value_get }, /* union get_value */
float_ieee_11073_cmp_eq,
float_ieee_11073_cmp_ne,
float_ieee_11073_cmp_gt,
float_ieee_11073_cmp_ge,
float_ieee_11073_cmp_lt,
float_ieee_11073_cmp_le,
float_ieee_11073_cmp_bitwise_and, /* cmp_bitwise_and */
NULL, /* cmp_contains */
NULL, /* cmp_matches */
NULL, /* len */
NULL, /* slice */
};
ftype_register(FT_IEEE_11073_SFLOAT, &sfloat_type);
ftype_register(FT_IEEE_11073_FLOAT, &float_type);
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/