/* From David Wijnants */ /* * This is an implementation of the OpenVMS cvt$convert_float routine * which converts single and double precision IEEE floats to and from * VAX f and d floats. * * It does not handle Cray and IBM types, and does not implement all * the rounding options in full. If you throw it lots of duff PC data, * you'll also find it doesn't do denormalised floats, but then it does * work reasonably well on normal everyday IEEE and VAX floats, which * is what I'm really after. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <cvtdef.h> #include <cvt$routines.h> /* * The VAX f and d-floats, and the IEEE s-float, have an eight-bit * exponent. Converting between these types just means rearranging the * bits. The following bitfield structures remove the need for masking * and shifting. */ typedef struct { unsigned f1 : 7; unsigned exp : 8; unsigned sign : 1; unsigned f2 : 16; } _VAXF; typedef struct { unsigned f1 : 7; unsigned exp : 8; unsigned sign : 1; unsigned f2 : 16; unsigned f3 : 16; unsigned f4 : 16; } _VAXD; typedef struct { unsigned f2 : 16; unsigned f1 : 7; unsigned exp : 8; unsigned sign : 1; } _IEES; /* * The IEEE t-float has an eleven-bit exponent. Converting to or from * this type requires rounding or truncation in relation to the extra * three bits of precision. The _FRACT bitfield union makes it easier * to add and remove three extra bits. */ typedef struct { unsigned f4 : 16; unsigned f3 : 16; unsigned f2 : 16; unsigned f1 : 4; unsigned exp : 11; unsigned sign : 1; } _IEET; typedef union { struct { unsigned five : 9; unsigned four : 16; unsigned three : 16; unsigned two : 16; unsigned one : 7; } exp8; struct { unsigned five : 12; unsigned four : 16; unsigned three : 16; unsigned two : 16; unsigned one : 4; } exp11; } _FRAC; /* * CVT$CONVERT_FLOAT is a general-purpose OpenVMS floating-point * conversion routine that converts VAX, IEEE, Cray and IBM * floating-point types to and from each other. * * ~iThis~i implementation provides only the 'useful' subset that * allows IEEE and VAX types to be converted on platforms other * than OpenVMS. */ int cvt$convert_float ( void * ival, int itype, void * oval, int otype, int opt ) { int inf=0, // input float is infinity zero=0, // set output float to zero over=0, // overflow detected nan=0, // input float is not a number roprand=0, // set output to 'reserved operand' denorm=0, // input float is denormalised exp; // (signed) zero'ed exponent unsigned int *l1, // lowest longword of float *l2; // next longword unsigned short sign; // 0=positive 1=negative _FRAC f; // fraction bits /* * I don't quite understand the ins and outs of the options * parameter, but it's probably mostly to do with what happens to * denormalised IEEEs - which aren't supported anyway - so I'll * just ignore it for now and truncate extraneous bits. Just for * consistency, I ~iwill~i check for an invalid parameter though. */ if ( opt & ~( CVT$M_ROUND_TO_NEAREST | CVT$M_ROUND_TO_ZERO | CVT$M_ROUND_TO_POS | CVT$M_ROUND_TO_NEG | CVT$M_VAX_ROUNDING | CVT$M_BIG_ENDIAN | CVT$M_ERR_UNDERFLOW ) ) return (CVT$_INVOPT); opt = CVT$M_ROUND_TO_ZERO; /* * Extract the bits from the input float, de-bias the exponent, and * recognise known bit patterns for zero, NaN and infinity. */ l2 = (l1=ival) + 1; switch ( itype ) { case CVT$K_VAX_F: { _VAXF *p = ival; sign = p->sign; exp = p->exp; f.exp8.one = p->f1; f.exp8.two = p->f2; f.exp8.three = 0; f.exp8.four = 0; f.exp8.five = 0; nan = ( *l1==0x00008000 ); zero = ( exp == 0 || exp >= 254 ) && !nan; exp -= 128; } break; case CVT$K_VAX_D: { _VAXD *p = ival; sign = p->sign; exp = p->exp; f.exp8.one = p->f1; f.exp8.two = p->f2; f.exp8.three = p->f3; f.exp8.four = p->f4; f.exp8.five = 0; nan = ( *l1==0x00008000 && *l2==0x00000000 ); zero = ( exp == 0 || exp >= 254 ) && !nan; exp -= 128; } break; case CVT$K_IEEE_S: { _IEES *p = ival; sign = p->sign; exp = p->exp; f.exp8.one = p->f1; f.exp8.two = p->f2; f.exp8.three = 0; f.exp8.four = 0; f.exp8.five = 0; zero = ( *l1==0x00000000 || *l1==0x80000000 ); inf = ( *l1==0x7f800000 || *l1==0xff800000 ); denorm = ( !exp && !zero ); over = ( exp == 0xfe ); nan = ( exp == 0xff ); roprand = ( over || nan ); exp -= 126; } break; case CVT$K_IEEE_T: { _IEET *p = ival; sign = p->sign; exp = p->exp; f.exp11.one = p->f1; f.exp11.two = p->f2; f.exp11.three = p->f3; f.exp11.four = p->f4; f.exp11.five = 0; zero = ( *l2==0x00000000 || *l2==0x80000000 ) && !*l1; inf = ( *l2==0x7ff00000 || *l2==0xfff00000 ) && !*l1; denorm = ( !exp && !zero ); over = ( exp == 0x7fe ); nan = ( exp == 0x7ff ); roprand = ( over || nan ); exp -= 1022; } break; default: return (CVT$_INVINPTYP); } /* * Poke the bits into the output float after re-biasing the exponent, * removal or addition of extra bits for t-floats is taken care of by * the _FRACT bit-field union. Zero and NaN are handled separately. */ l2 = (l1=oval) + 1; switch ( otype ) { case CVT$K_VAX_F: if ( zero ) *l1 = 0x00000000; else if ( roprand ) *l1 = 0x00008000; else { _VAXF *p = oval; f.exp8.three = 0; p->sign = sign; p->exp = exp + 128; p->f1 = f.exp8.one; p->f2 = f.exp8.two; } break; case CVT$K_VAX_D: if ( zero ) *l2 = 0x00000000, *l1 = 0x00000000; else if ( roprand ) *l2 = 0x00000000, *l1 = 0x00008000; else { _VAXD *p = oval; f.exp8.five = 0; p->sign = sign; p->exp = exp + 128; p->f1 = f.exp8.one; p->f2 = f.exp8.two; p->f3 = f.exp8.three; p->f4 = f.exp8.four; } break; case CVT$K_IEEE_S: if ( nan ) *l1 = 0x7fbfffff; else if ( zero ) *l1 = 0x00000000; else { _IEES *p = oval; f.exp8.three = 0; p->sign = sign; p->exp = exp + 126; p->f1 = f.exp8.one; p->f2 = f.exp8.two; } break; case CVT$K_IEEE_T: if ( nan ) *l2 = 0x7ff7ffff, *l1 = 0xffffffff; else if ( zero ) *l2 = 0x00000000, *l1 = 0x00000000; else { _IEET *p = oval; f.exp11.five = 0; p->sign = sign; p->exp = exp + 1022; p->f1 = f.exp11.one; p->f2 = f.exp11.two; p->f3 = f.exp11.three; p->f4 = f.exp11.four; } break; default: return (CVT$_INVOUTTYP); } /* * Return status codes for negative infinity, positive infinity, * invalid number or NaN, and overflow detected. In the absence of * any strange conditions, indicate normal successful completion. */ if ( inf && sign ) return (CVT$_NEGINF); if ( inf && !sign ) return (CVT$_POSINF); if ( nan ) return (CVT$_INVVAL); if ( over ) return (CVT$_OVERFLOW); return (CVT$_NORMAL); }