把一个IEEE754浮点数转换为IBM370浮点数的C#代码
把一个IEEE754浮点数转换为IBM370浮点数的C#代码。
在这个网页上有古老的IBM370浮点格式的说明。
// http://en.wikipedia.org/wiki/IBM_Floating_Point_Architecture // float2ibm(-118.625F) == 0xC276A000 // 1 100 0010 0111 0110 1010 0000 0000 0000 // IBM/370 single precision, 4 bytes // xxxx.xxxx xxxx.xxxx xxxx.xxxx xxxx.xxxx // s|-exp--| |--------fraction-----------| // (7) (24) // value = (-1)**s * 16**(e - 64) * .f range = 5E-79 ... 7E+75 static int float2ibm(float from) { byte[] bytes = BitConverter.GetBytes(from); int fconv = (bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8)| bytes[0]; if (fconv == 0) return 0; int fmant = (0x007fffff & fconv) | 0x00800000; int t = (int)((0x7f800000 & fconv) >> 23) - 126; while (0 != (t & 0x3)) { ++t; fmant >>= 1; } fconv = (int)(0x80000000 & fconv) | (((t >> 2) + 64) << 24) | fmant; return fconv; // big endian order }
在这段C代码的基础上修改的
/* Assumes sizeof(int) == 4 */ static void float_to_ibm(int from[], int to[], int n, int endian) /********************************************************************** float_to_ibm - convert between 32 bit IBM and IEEE floating numbers *********************************************************************** Input: from input vector n number of floats in vectors endian =0 for little endian machine, =1 for big endian machines Output: to output vector, can be same as input vector *********************************************************************** Notes: Up to 3 bits lost on IEEE -> IBM IBM -> IEEE may overflow or underflow, taken care of by substituting large number or zero Only integer shifting and masking are used. *********************************************************************** Credits: CWP: Brian Sumner ***********************************************************************/ { register int fconv, fmant, i, t; for (i=0;i<n;++i) { fconv = from[i]; if (fconv) { fmant = (0x007fffff & fconv) | 0x00800000; t = (int) ((0x7f800000 & fconv) >> 23) - 126; while (t & 0x3) { ++t; fmant >>= 1; } fconv = (0x80000000 & fconv) | (((t>>2) + 64) << 24) | fmant; } if(endian==0) fconv = (fconv<<24) | ((fconv>>24)&0xff) | ((fconv&0xff00)<<8) | ((fconv&0xff0000)>>8); to[i] = fconv; } return; }
另外找到的一些C代码放在这里以后备用
#include "ibmieee.h" /*--------------------------------------------------------------------- * * FILE: ./ibmieee.c * *---------------------------------------------------------------------- * * DESCRIPTION: * Conversion from IBM to IEEE format * Byte order is detected and accounted for using * define constant 'L_ENDIAN' * *---------------------------------------------------------------------- * ROUTINES DEFINED IN THIS MODULE: *---------------------------------------------------------------------- * * * Convert IBM to IEEE integer: * * ibm_ieee_i( n, ibm, ieee) * int n; * int *ibm; * int *ieee; * * Convert IEEE to IBM integer: * * ieee_ibm_i( n, ieee, ibm) * int n; * int *ieee; * int *ibm; * * Convert IBM to IEEE short floating point: * * ibm_ieee_f( n, ibm, ieee) * int n; * char *ibm; * float *ieee; * * Convert IEEE to IBM floating point: * * ieee_ibm_f( n, ieee, ibm) * int n; * float *ieee; * char *ibm; * *---------------------------------------------------------------------*/ void ibm_ieee_i( int n, char *ibm, char *ieee ) /* Convert from IBM to IEEE integer, possibly in-place */ #ifdef L_ENDIAN { int j; char temp; /* Reverse byte order on little endian machines */ for (j=0; j<4*n; j+=4) { temp=ibm[j]; ieee[j]=ibm[j+3]; ieee[j+3]=temp; temp=ibm[j+1]; ieee[j+1]=ibm[j+2]; ieee[j+2]=temp; } #else { int j; /* Move words if not same location */ if (ibm != ieee) for (j=0; j<n; j++) ieee[j] = ibm[j]; #endif /* L_ENDIAN */ return ; } /*********************************************************************/ void ieee_ibm_i( int n, char *ibm, char *ieee ) /* Convert from IEEE to IBM integer, possibly in-place */ #ifdef L_ENDIAN { int j; char temp; /* Reverse byte order on little endian machines */ for (j=0; j<4*n; j+=4) { temp=ieee[j]; ibm[j]=ieee[j+3]; ibm[j+3]=temp; temp=ieee[j+1]; ibm[j+1]=ieee[j+2]; ibm[j+2]=temp; } #else { int j; /* Move words if not same location */ if (ibm != ieee) for (j=0; j<n; j++) ibm[j] = ieee[j]; #endif /* L_ENDIAN */ return ; } /*--------------------------------------------------------------------- * * Convert from IBM short real to IEEE short real * * * Bit formats for Big Endian processors (i.e. 68020, SPARC, RS6000): * * 0 1 2 3 Byte Offset * -------------------------------------- * IBM !S! EXP ! MH ! MM ! ML ! * -------------------------------------- * 0 1 7 8 15 16 23 24 31 bit number * * 0 1 2 3 Byte Offset * -------------------------------------- * IEEE !S! EXP ! MH ! MM ! ML ! * -------------------------------------- * 0 1 8 9 15 16 23 24 31 bit number * * * Bit formats for Little Endian processors (i.e. Intel 80x86): * * 3 2 1 0 byte offset * -------------------------------------- * IBM ! ML ! MH ! MH !S! EXP ! * -------------------------------------- * 31 24 23 16 15 8 7 6 0 bit number * * 3 2 1 0 byte offset * -------------------------------------- * IEEE !S! EXP !! MH ! MM ! ML ! * -------------------------------------- * 31 30 23 22 16 15 8 7 0 bit number * * * ML, MM, and MH refer to the low, middle, and high order mantissa * bytes. S is the sign bit, EXP is the exponent. Bits and bytes * are numbered in order of increasing address in memory. The fields * are shown in the order that a Hex constant in C would be used as * a mask for accessing bits. * * * A floating point number in the IBM format is defined to be: * * (sign) 0.(significand) * 16**(exponent-64) * * In the IEEE format, a floating point number is: * * (sign) 1.(significand) * 2**(exponent-127) * * The differences are: * * 1. IBM assumes a 0 to left of decimal point, * IEEE assumes a 1 to left of decimal point. * Since the IBM format has 1 more bit for the significand, * they both have the same precision: 24 binary digits. * * 2. The base for the exponent is 16 for IBM, 2 for IEEE * * 3. The bias for the exponent is 64 for IBM, 127 for IEEE * * 4. The exponent is 7 bits for IBM, 8 for IEEE. The exponent * range is then -64 to +63 for IBM, -127 to +128 for IEEE. * * 5. Since the IBM format has base 16, the decimal exponent * range for IBM is about twice that for IEEE: * IBM: 0.1H * 16**(-64) to 0.FFFFFFH * 16**(+63) * = 5.4 * 10**(-79) to 7.2 * 10**(+75) * IEEE: 1.0H * 2**(-127) to 1.FFFFFFH * 2**(+128) * = 5.9 * 10**(-39) to 6.8 * 10**(+38) * * To convert IBM to IEEE: * * 1. Multiply IBM exponent by 4 to convert base from 16 to 2, * subtract 129 to adjust for difference in bias (16*4-127). * * 2. Shift the IBM significand left 1 bit and subtract 1 from * the exponent. If the significand MSB is still 0, repeat. * * 3. If the exponent overflows, set result to +-infinity. If * it underflows, set to +-zero. * * 4. Move the converted sign, significand, and exponent into * the appropriate fields for the IEEE format. * * * To convert IEEE to IBM: * * 1. Get IEEE mantissa (bits 0-22). OR in a 1 in bit 23 to * account for assumed leading '1'. * * 2. Get IEEE exponent. Add 1 to exponent to place the decimal * point before the '1' in the mantissa. Subtract IEEE bias * of 127 to yield true base 2 exponent. * * 3. Divide IEEE exponent by 4 to convert to base 16 exponent: * 0.X * 2**Y = 0.X * 16**(Y/4) * Save remainder from division. If remainder is not zero: * 0.X * 16**(I+1/4) = 2*(0.X) * 16**I = (1/8)0.X * 16**(I+1) * 0.X * 16**(I+2/4) = 4*(0.X) * 16**I = (1/4)0.X * 16**(I+1) * 0.X * 16**(I+3/4) = 8*(0.X) * 16**I = (1/2)0.X * 16**(I+1) * I.E., add 1 to exponent, shift mantissa right by * (4 - remainder). Add IBM bias of 64 to exponent. * * 4. Move the converted sign, significand, and exponent into * the appropriate fields for the IBM format. * * *---------------------------------------------------------------------*/ void ibm_ieee_f( int n, VAL *ibm, VAL *ieee ) { int i,iret; VAL tmp1,tmp2,temp; int exp; iret = 0; for (i=0; i<n; i++) { /* Move IBM word to properly aligned temp location */ temp.c[0] = ibm[i].c[0]; temp.c[1] = ibm[i].c[1]; temp.c[2] = ibm[i].c[2]; temp.c[3] = ibm[i].c[3]; /* check for true zero */ if (temp.u == 0) { ieee[i].c[0] = ieee[i].c[1] = ieee[i].c[2] = ieee[i].c[3] = '\0'; continue; } /* extract mantissa */ #ifdef L_ENDIAN tmp1.c[0] = temp.c[3]; tmp1.c[1] = temp.c[2]; tmp1.c[2] = temp.c[1]; tmp1.c[3] = '\0'; #else tmp1.u = temp.u; tmp1.c[0] = '\0'; #endif /* Extract exponent */ /* remove sign bit, multiply by 4, subtract bias difference */ exp = ((temp.c[0] & 0x7F) << 2 ) - 129; /* Shift mantissa up 1 bit until there is a 1 in the MSB */ while ( (tmp1.u & 0x800000) == 0 ) { exp--; tmp1.u = tmp1.u << 1; }; /* Discard MSB and decrement exponent 1 more */ tmp1.u = tmp1.u & 0x007FFFFF; exp--; /* Check for exponent overflow or underflow */ if (exp & 0xFF00) { if (exp < 0) /* Underflow */ ieee[i].c[0] = ieee[i].c[1] = ieee[i].c[2] = ieee[i].c[3] = '\0'; else { /* Overflow */ #ifdef L_ENDIAN ieee[i].c[3] = 0x7F; ieee[i].c[2] = 0x80; ieee[i].c[1] = 0x00; ieee[i].c[0] = 0x00; #else ieee[i].c[0] = 0x7F; ieee[i].c[1] = 0x80; ieee[i].c[2] = 0x00; ieee[i].c[3] = 0x00; #endif } iret = 1; continue; } /* Move exponent into proper field and set sign bit */ tmp2.u = 0; #ifdef L_ENDIAN tmp2.c[3] = (char)exp; tmp2.u = tmp2.u >> 1; if (temp.u & 0x00000080) tmp2.u = tmp2.u | 0x80000000; else tmp2.u = tmp2.u & 0x7fffffff; #else tmp2.c[0] = (char)exp; tmp2.u = tmp2.u >> 1; tmp2.u = tmp2.u | (temp.u & 0x80000000); #endif /* Set output value */ temp.u = tmp1.u | tmp2.u; ieee[i].c[0] = temp.c[0]; ieee[i].c[1] = temp.c[1]; ieee[i].c[2] = temp.c[2]; ieee[i].c[3] = temp.c[3]; } return ; } /*********************************************************************/ void ieee_ibm_f( int n, VAL *ieee, VAL *ibm ) { int i; VAL temp,tmp1,tmp2; int exp,rem; for (i=0; i<n; i++) { /* Move to properly aligned location */ temp.c[0] = ieee[i].c[0]; temp.c[1] = ieee[i].c[1]; temp.c[2] = ieee[i].c[2]; temp.c[3] = ieee[i].c[3]; /* check for true zero */ if (temp.u == 0) { ibm[i].c[0] = ibm[i].c[1] = ibm[i].c[2] = ibm[i].c[3] = '\0'; continue; } /* extract mantissa, OR in leading '1' */ tmp1.u = (temp.u & 0x007FFFFF) | 0x00800000; /* Extract exponent */ /* remove sign bit, shift up one bit */ tmp2.u = (temp.u & 0x7F800000) << 1; /* Add 1 for mantissa shift, subtract 127 for IEEE bias */ #ifdef L_ENDIAN exp = (unsigned)tmp2.c[3] - 126; #else exp = (unsigned)tmp2.c[0] - 126; #endif /* Divide exponent by 4, save remainder, add IBM bias of 64 */ rem = exp & 0x03; exp = (exp >> 2) + 64; /* Normalize mantissa by shifting by (4-remainder) */ if (rem) { tmp1.u = tmp1.u >> (4-rem); exp = exp + 1; } /* Move exponent and mantissa bytes into IBM locations */ #ifdef L_ENDIAN tmp2.u = 0; tmp2.c[0] = (char)exp; tmp2.c[1] = tmp1.c[2]; tmp2.c[2] = tmp1.c[1]; tmp2.c[3] = tmp1.c[0]; /* Set sign bit */ if (temp.u & 0x80000000) tmp2.u = tmp2.u | 0x00000080; #else tmp2.u = tmp1.u; tmp2.c[0] = (char)exp; /* Set sign bit */ if (temp.u & 0x80000000) tmp2.u = tmp2.u | 0x80000000; #endif /* Set output value */ ibm[i].c[0] = tmp2.c[0]; ibm[i].c[1] = tmp2.c[1]; ibm[i].c[2] = tmp2.c[2]; ibm[i].c[3] = tmp2.c[3]; } return ; }
----==== Email: slofslb (GTD) qq.com 请将(GTD)换成@ ====----
版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
作者:申龙斌的程序人生
---- 魔方、桥牌、象棋、游戏人生...
---- BASIC、C++、JAVA、C#、Haskell、Objective-C、Open Inventor、程序人生...
---- GTD伴我实现人生目标
---- 区块链生存训练
---- 用欧拉计划学Rust编程
---- 申龙斌的读书笔记(2011-2019)
----