#include <ctype.h>
#include <stdlib.h>
#include <math.h>
#include "sics.h"

/*
 * conversions:
 * hex2float
 * float2hex
 *
 * Markus Zolliker Aug 2010
 */

/*-------------------------------------------------------------------------*/
void double2ieee(double input, char ieee[4])
{

/* convert double to IEEE 32 bit floating number (denormalized numbers are considered as zero) */

  long mantissa;
  int exponent;

  if (input == 0) {
    ieee[0] = 0;
    ieee[1] = 0;
    ieee[2] = 0;
    ieee[3] = 0;
  } else {
    mantissa = 0x1000000 * (frexp(fabs(input), &exponent));
    exponent = exponent - 1 + 127;
    if (exponent < 0) {
      exponent = 0;
    } else if (exponent > 0xFE) {
      exponent = 0xFE;
    }
    if (input < 0) {
      ieee[0] = 0x80 | (exponent >> 1);
    } else {
      ieee[0] = exponent >> 1;
    }
    ieee[1] = (exponent & 1) << 7 | ((mantissa & 0x7F0000) >> 16);
    ieee[2] = (mantissa & 0xFF00) >> 8;
    ieee[3] = mantissa & 0xFF;
  }
  return;
}

/*-------------------------------------------------------------------------*/
double ieee2double(char ieee[4])
{

/* IEEE 32 bit floating number to double (denormalized numbers are considered as zero) */

  long mantissa;
  double output;
  int exponent;

  mantissa = ((ieee[1] << 16) & 0x7FFFFF)
      | ((ieee[2] << 8) & 0xFF00)
      | ((ieee[3]) & 0xFF);

  exponent = (ieee[0] & 0x7F) * 2 + ((ieee[1] >> 7) & 1);       /* raw exponent */
  if (exponent == 0 && mantissa == 0) {
    return 0.0;
  }
  output = ldexp(mantissa, -23) + 1.0;
  if (ieee[0] & 0x80) {
    output = -output;
  }
  return output * ldexp(1, exponent - 127);
}

/*-------------------------------------------------------------------------*/
int CnvrtAction(SConnection *con, SicsInterp *sics, void *data,
                int argc, char *argv[]) {
  double f;
  char ieee[4];
  char result[32];

  if (argc != 3) goto Usage;
  if (strcasecmp(argv[1], "float2xieee") == 0) {
    double2ieee(atof(argv[2]), ieee);
    SCPrintf(con, eValue, "%2.2x%2.2x%2.2x%2.2x",
             ieee[0] & 0xFF, ieee[1] & 0xFF, ieee[2] & 0xFF, ieee[3] & 0xFF);
    return 1;
  } else if (strcasecmp(argv[1], "xieee2float") == 0) {
    ieee[0]=0;
    ieee[1]=0;
    ieee[2]=0;
    ieee[3]=0;
    sscanf(argv[2], "%2hhx%2hhx%2hhx%2hhx",
       ieee, ieee+1, ieee+2, ieee+3);
    snprintf(result, sizeof result, "%.7g", ieee2double(ieee));
    if (strchr(result, '.') == NULL && strchr(result, 'e') == NULL) {
      SCPrintf(con, eValue, "%s.", result);
    } else {
      SCWrite(con, result, eValue);
    }
    return 1;
  }
  Usage:
  SCPrintf(con, eError, "ERROR: Usage: cnvrt float2xieee <float>");
  SCPrintf(con, eError, "              cnvrt xieee2float <hexadecimal ieee>");
  return 0;
}
