/* * rpf.c: Conversion of float to reduced precision format values * * Written by: Stefan Frank * Richard Krampfl * Ullrich Hafner * * This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec) * Copyright (C) 1994-2000 Ullrich Hafner */ /* * $Date: 2000/06/14 20:49:37 $ * $Author: hafner $ * $Revision: 5.1 $ * $State: Exp $ */ #include "pm_config.h" #include "config.h" #include "mallocvar.h" #include "types.h" #include "macros.h" #include "error.h" #include "misc.h" #include "rpf.h" int const RPF_ZERO = -1; /***************************************************************************** private code *****************************************************************************/ typedef struct { double fraction; int exponent; } FracExp; static FracExp fracExpFromDouble(double const x) { FracExp retval; retval.fraction = frexp(x, &retval.exponent); return retval; } int rtob (real_t const f, const rpf_t * const rpfP) /* * Convert real number 'f' into fixed point format. * The real number in [-'range'; +'range'] is scaled to [-1 ; +1]. * Sign and the first 'precision' - 1 bits of the mantissa are * packed into one integer. * * Return value: * real value in reduced precision format */ { /* * Extract mantissa (23 Bits), exponent (8 Bits) and sign (1 Bit) */ double const normalized = f / rpfP->range; /* 'f' scaled to [-1,+1] */ FracExp const fracExp = fracExpFromDouble(normalized); unsigned int const signedMantissa = (unsigned int) (fracExp.fraction * (1<<23)); unsigned int mantissa; unsigned int sign; /* 0 for positive; 1 for negative */ if (signedMantissa < 0) { mantissa = -signedMantissa; sign = 1; } else { mantissa = +signedMantissa; sign = 0; } /* * Generate reduced precision mantissa. */ if (fracExp.exponent > 0) mantissa <<= fracExp.exponent; else mantissa >>= -fracExp.exponent; mantissa >>= (23 - rpfP->mantissa_bits - 1); mantissa += 1; /* Round last bit. */ mantissa >>= 1; if (mantissa == 0) /* close to zero */ return RPF_ZERO; else if (mantissa >= (1U << rpfP->mantissa_bits)) /* overflow */ return sign; else return ((mantissa & ((1U << rpfP->mantissa_bits) - 1)) << 1) | sign; } float btor (int const binary, const rpf_t * const rpfP) /* * Convert value 'binary' in reduced precision format to a real value. * For more information refer to function rtob() above. * * Return value: * converted value */ { unsigned int mantissa; float sign; float f; if (binary == RPF_ZERO) return 0; if (binary < 0 || binary >= 1 << (rpfP->mantissa_bits + 1)) error ("Reduced precision format: value %d out of range.", binary); /* * Restore IEEE float format: * mantissa (23 Bits), exponent (8 Bits) and sign (1 Bit) */ sign = (binary & 0x1) == 0 ? 1.0 : -1.0; mantissa = (binary & ((0x1 << (rpfP->mantissa_bits + 1)) - 1)) >> 1; mantissa <<= (23 - rpfP->mantissa_bits); if (mantissa == 0) f = sign; else f = sign * (float) mantissa / 8388608; return f * rpfP->range; /* expand [ -1 ; +1 ] to [ -range ; +range ] */ } rpf_t * alloc_rpf (unsigned const mantissa, fiasco_rpf_range_e const range) /* * Reduced precision format constructor. * Allocate memory for the rpf_t structure. * Number of mantissa bits is given by `mantissa'. * The range of the real values is in the interval [-`range', +`range']. * In case of invalid parameters, a structure with default values is * returned. * * Return value * pointer to the new rpf structure */ { rpf_t * rpfP; MALLOCVAR(rpfP); if (mantissa < 2) { warning (_("Size of RPF mantissa has to be in the interval [2,8]. " "Using minimum value 2.\n")); rpfP->mantissa_bits = 2; } else if (mantissa > 8) { warning (_("Size of RPF mantissa has to be in the interval [2,8]. " "Using maximum value 8.\n")); rpfP->mantissa_bits = 2; } else rpfP->mantissa_bits = mantissa; switch (range) { case FIASCO_RPF_RANGE_0_75: rpfP->range = 0.75; rpfP->range_e = range; break; case FIASCO_RPF_RANGE_1_50: rpfP->range = 1.50; rpfP->range_e = range; break; case FIASCO_RPF_RANGE_2_00: rpfP->range = 2.00; rpfP->range_e = range; break; case FIASCO_RPF_RANGE_1_00: rpfP->range = 1.00; rpfP->range_e = range; break; default: warning (_("Invalid RPF range specified. Using default value 1.0.")); rpfP->range = 1.00; rpfP->range_e = FIASCO_RPF_RANGE_1_00; break; } return rpfP; }