1 #include <stdint.h>
2 #include <string.h>
3 
4 #include "ieee.h"
5 #include "../readstat_bits.h"
6 
7 /* These routines are modified versions of those found in SAS publication TS-140,
8  * "RECORD LAYOUT OF A SAS VERSION 5 OR 6 DATA SET IN SAS TRANSPORT (XPORT) FORMAT"
9  * https://support.sas.com/techsup/technote/ts140.pdf
10  *
11  * Modifications include using stdint.h and supporting infinite IEEE values.
12  */
13 
14 static void xpt2ieee(unsigned char *xport, unsigned char *ieee);
15 static void ieee2xpt(unsigned char *ieee, unsigned char *xport);
16 
17 #ifndef FLOATREP
18 #define FLOATREP get_native()
19 int get_native();
20 #endif
21 
memreverse(void * intp_void,int l)22 void memreverse(void *intp_void, int l) {
23     if (!machine_is_little_endian())
24         return;
25 
26     int i,j;
27     char save;
28     char *intp = (char *)intp_void;
29 
30     j = l/2;
31     for (i=0;i<j;i++) {
32         save = intp[i];
33         intp[i] = intp[l-i-1];
34         intp[l-i-1] = save;
35     }
36 }
37 
cnxptiee(const void * from_bytes,int fromtype,void * to_bytes,int totype)38 int cnxptiee(const void *from_bytes, int fromtype, void *to_bytes, int totype)
39 {
40     unsigned char *from = (unsigned char *)from_bytes;
41     unsigned char *to = (unsigned char *)to_bytes;
42     unsigned char temp[8];
43     int i;
44 
45     if (fromtype == CN_TYPE_NATIVE) {
46         fromtype = FLOATREP;
47     }
48     switch(fromtype) {
49         case CN_TYPE_IEEEL :
50             if (totype == CN_TYPE_IEEEL)
51                 break;
52             for (i=7;i>=0;i--) {
53                 temp[7-i] = from[i];
54             }
55             from = temp;
56             fromtype = CN_TYPE_IEEEB;
57             /* Break intentionally omitted. */
58         case CN_TYPE_IEEEB :
59             /* Break intentionally omitted. */
60         case CN_TYPE_XPORT :
61             break;
62         default:
63             return(-1);
64     }
65     if (totype == CN_TYPE_NATIVE) {
66         totype = FLOATREP;
67     }
68     switch(totype) {
69         case CN_TYPE_XPORT :
70         case CN_TYPE_IEEEB :
71         case CN_TYPE_IEEEL :
72             break;
73         default:
74             return(-2);
75     }
76     if (fromtype == totype) {
77         memcpy(to,from,8);
78         return(0);
79     }
80     switch(fromtype) {
81         case CN_TYPE_IEEEB :
82             if (totype == CN_TYPE_XPORT)
83                 ieee2xpt(from,to);
84             else memcpy(to,from,8);
85             break;
86         case CN_TYPE_XPORT :
87             xpt2ieee(from,to);
88             break;
89     }
90     if (totype == CN_TYPE_IEEEL) {
91         memcpy(temp,to,8);
92         for (i=7;i>=0;i--) {
93             to[7-i] = temp[i];
94         }
95     }
96     return(0);
97 }
98 
get_native()99 int get_native() {
100     static unsigned char float_reps[][8] = {
101         {0x41,0x10,0x00,0x00,0x00,0x00,0x00,0x00},
102         {0x3f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00},
103         {0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x3f}
104     };
105 
106     static double one = 1.00;
107 
108     int i,j;
109     j = sizeof(float_reps)/8;
110     for (i=0;i<j;i++)  {
111         if (memcmp(&one,float_reps+i,8) == 0)
112             return(i+1);
113     }
114     return(-1);
115 }
116 
xpt2ieee(unsigned char * xport,unsigned char * ieee)117 void xpt2ieee(unsigned char *xport, unsigned char *ieee) {
118     char temp[8];
119     register int shift;
120     register int nib;
121     uint32_t ieee1,ieee2;
122     uint32_t xport1 = 0;
123     uint32_t xport2 = 0;
124 
125     memcpy(temp,xport,8);
126     memset(ieee,0,8);
127 
128     if (*temp && memcmp(temp+1,ieee,7) == 0) {
129         ieee[0] = ieee[1] = 0xff;
130         ieee[2] = ~(*temp);
131         return;
132     }
133 
134     memcpy(&xport1,temp,sizeof(uint32_t));
135     memreverse(&xport1,sizeof(uint32_t));
136     memcpy(&xport2,temp+4,sizeof(uint32_t));
137     memreverse(&xport2,sizeof(uint32_t));
138 
139     /***************************************************************/
140     /* Translate IBM format floating point numbers into IEEE */
141     /* format floating point numbers. */
142     /* */
143     /* IEEE format: */
144     /* */
145     /* 6 5 0 */
146     /* 3 1 0 */
147     /* */
148     /* SEEEEEEEEEEEMMMM ............ MMMM */
149     /* */
150     /* Sign bit, 11 bits exponent, 52 bit fraction. Exponent is */
151     /* excess 1023. The fraction is multiplied by a power of 2 of */
152 
153     /* the actual exponent. Normalized floating point numbers are */
154     /* represented with the binary point immediately to the left */
155     /* of the fraction with an implied "1" to the left of the */
156     /* binary point. */
157     /* */
158     /* IBM format: */
159     /* */
160     /* 6 5 0 */
161     /* 3 1 0 */
162     /* */
163     /* SEEEEEEEMMMM ......... MMMM */
164     /* */
165     /* Sign bit, 7 bit exponent, 56 bit fraction. Exponent is */
166     /* excess 64. The fraction is multiplied bya power of 16 of */
167     /* the actual exponent. Normalized floating point numbers are */
168     /* represented with the radix point immediately to the left of*/
169     /* the high order hex fraction digit. */
170     /* */
171     /* How do you translate from IBM format to IEEE? */
172     /* */
173     /* Translating back to ieee format from ibm is easier than */
174     /* going the other way. You lose at most, 3 bits of fraction, */
175     /* but nothing can be done about that. The only tricky parts */
176     /* are setting up the correct binary exponent from the ibm */
177     /* hex exponent, and removing the implicit "1" bit of the ieee*/
178     /* fraction (see vzctdbl). We must shift down the high order */
179     /* nibble of the ibm fraction until it is 1. This is the */
180     /* implicit 1. The bit is then cleared and the exponent */
181     /* adjusted by the number of positions shifted. A more */
182     /* thorough discussion is in vzctdbl.c. */
183 
184     if ((xport1 & 0x7fffffff) == 0x7fffffff && xport2 == 0xffffffff) {
185         ieee1 = (xport1 & 0x80000000) | 0x7ff00000;
186         ieee2 = 0;
187         goto doret;
188     }
189 
190     /* Get the first half of the ibm number without the exponent */
191     /* into the ieee number */
192     ieee1 = xport1 & 0x00ffffff;
193 
194     /* get the second half of the ibm number into the second half */
195     /* of the ieee number . If both halves were 0. then just */
196     /* return since the ieee number is zero. */
197     if ((!(ieee2 = xport2)) && !xport1)
198         return;
199 
200     /* The fraction bit to the left of the binary point in the */
201     /* ieee format was set and the number was shifted 0, 1, 2, or */
202     /* 3 places. This will tell us how to adjust the ibm exponent */
203     /* to be a power of 2 ieee exponent and how to shift the */
204     /* fraction bits to restore the correct magnitude. */
205 
206     if ((nib = (int)xport1) & 0x00800000) {
207         shift = 3;
208     } else if (nib & 0x00400000) {
209         shift = 2;
210     } else if (nib & 0x00200000) {
211         shift = 1;
212     } else {
213         shift = 0;
214     }
215 
216     if (shift) {
217         /* shift the ieee number down the correct number of places */
218         /* then set the second half of the ieee number to be the */
219         /* second half of the ibm number shifted appropriately, */
220         /* ored with the bits from the first half that would have */
221         /* been shifted in if we could shift a double. All we are */
222         /* worried about are the low order 3 bits of the first */
223         /* half since we're only shifting by 1, 2, or 3. */
224         ieee1 >>= shift;
225         ieee2 = (xport2 >> shift) |
226             ((xport1 & 0x00000007) << (29 + (3 - shift)));
227     }
228 
229     /* clear the 1 bit to the left of the binary point */
230     ieee1 &= 0xffefffff;
231 
232     /* set the exponent of the ieee number to be the actual */
233     /* exponent plus the shift count + 1023. Or this into the */
234     /* first half of the ieee number. The ibm exponent is excess */
235     /* 64 but is adjusted by 65 since during conversion to ibm */
236     /* format the exponent is incremented by 1 and the fraction */
237     /* bits left 4 positions to the right of the radix point. */
238     ieee1 |=
239         (((((int32_t)(*temp & 0x7f) - 65) * 4) + shift + 1023) << 20) |
240         (xport1 & 0x80000000);
241 
242 doret:
243     memreverse(&ieee1,sizeof(uint32_t));
244     memcpy(ieee,&ieee1,sizeof(uint32_t));
245     memreverse(&ieee2,sizeof(uint32_t));
246     memcpy(ieee+4,&ieee2,sizeof(uint32_t));
247     return;
248 }
249 
250 /*-------------------------------------------------------------*/
251 /* Name: ieee2xpt */
252 /* Purpose: converts IEEE to transport */
253 /* Usage: rc = ieee2xpt(to_ieee,p_data); */
254 /* Notes: this routine is an adaptation of the wzctdbl routine */
255 /* from the Apollo. */
256 /*-------------------------------------------------------------*/
257 
ieee2xpt(unsigned char * ieee,unsigned char * xport)258 void ieee2xpt(unsigned char *ieee, unsigned char *xport) {
259     register int shift;
260     unsigned char misschar;
261     int ieee_exp;
262     uint32_t xport1,xport2;
263     uint32_t ieee1 = 0;
264     uint32_t ieee2 = 0;
265 
266     char ieee8[8];
267 
268     memcpy(ieee8,ieee,8);
269 
270     /*------get 2 longs for shifting------------------------------*/
271     memcpy(&ieee1,ieee8,sizeof(uint32_t));
272     memreverse(&ieee1,sizeof(uint32_t));
273     memcpy(&ieee2,ieee8+4,sizeof(uint32_t));
274     memreverse(&ieee2,sizeof(uint32_t));
275 
276     memset(xport,0,8);
277 
278     /*-----if IEEE value is missing (1st 2 bytes are FFFF)-----*/
279     if (*ieee8 == (char)0xff && ieee8[1] == (char)0xff) {
280         misschar = ~ieee8[2];
281         *xport = (misschar == 0xD2) ? 0x6D : misschar;
282         return;
283     }
284 
285     /**************************************************************/
286     /* Translate IEEE floating point number into IBM format float */
287     /* */
288     /* IEEE format: */
289     /* */
290     /* 6 5 0 */
291     /* 3 1 0 */
292     /* */
293     /* SEEEEEEEEEEEMMMM ........ MMMM */
294     /* */
295     /* Sign bit, 11 bit exponent, 52 fraction. Exponent is excess */
296     /* 1023. The fraction is multiplied by a power of 2 of the */
297     /* actual exponent. Normalized floating point numbers are */
298     /* represented with the binary point immediately to the left */
299     /* of the fraction with an implied "1" to the left of the */
300     /* binary point. */
301     /* */
302     /* IBM format: */
303     /* */
304     /* 6 5 0 */
305     /* 3 5 0 */
306     /* */
307     /* SEEEEEEEMMMM ......... MMMM */
308     /* */
309     /* Sign bit, 7 bit exponent, 56 bit fraction. Exponent is */
310     /* excess 64. The fraction is multiplied by a power of 16 of */
311     /* of the actual exponent. Normalized floating point numbers */
312     /* are presented with the radix point immediately to the left */
313     /* of the high order hex fraction digit. */
314     /* */
315     /* How do you translate from local to IBM format? */
316     /* */
317     /* The ieee format gives you a number that has a power of 2 */
318     /* exponent and a fraction of the form "1.<fraction bits>". */
319     /* The first step is to get that "1" bit back into the */
320     /* fraction. Right shift it down 1 position, set the high */
321     /* order bit and reduce the binary exponent by 1. Now we have */
322 
323     /* a fraction that looks like ".1<fraction bits>" and it's */
324     /* ready to be shoved into ibm format. The ibm fraction has 4 */
325     /* more bits than the ieee, the ieee fraction must therefore */
326     /* be shifted left 4 positions before moving it in. We must */
327     /* also correct the fraction bits to account for the loss of 2*/
328     /* bits when converting from a binary exponent to a hex one */
329     /* (>> 2). We must shift the fraction left for 0, 1, 2, or 3 */
330     /* positions to maintain the proper magnitude. Doing */
331     /* conversion this way would tend to lose bits in the fraction*/
332     /* which is not desirable or necessary if we cheat a bit. */
333     /* First of all, we know that we are going to have to shift */
334     /* the ieee fraction left 4 places to put it in the right */
335     /* position; we won't do that, we'll just leave it where it is*/
336     /* and increment the ibm exponent by one, this will have the */
337     /* same effect and we won't have to do any shifting. Now, */
338     /* since we have 4 bits in front of the fraction to work with,*/
339     /* we won't lose any bits. We set the bit to the left of the */
340     /* fraction which is the implicit "1" in the ieee fraction. We*/
341     /* then adjust the fraction to account for the loss of bits */
342     /* when going to a hex exponent. This adjustment will never */
343     /* involve shifting by more than 3 positions so no bits are */
344     /* lost. */
345 
346     /* Get ieee number less the exponent into the first half of */
347     /* the ibm number */
348 
349     xport1 = ieee1 & 0x000fffff;
350 
351     /* get the second half of the number into the second half of */
352     /* the ibm number and see if both halves are 0. If so, ibm is */
353     /* also 0 and we just return */
354 
355     if ((!(xport2 = ieee2)) && !ieee1) {
356         ieee_exp = 0;
357         goto doret;
358     }
359 
360     /* get the actual exponent value out of the ieee number. The */
361     /* ibm fraction is a power of 16 and the ieee fraction a power*/
362     /* of 2 (16 ** n == 2 ** 4n). Save the low order 2 bits since */
363     /* they will get lost when we divide the exponent by 4 (right */
364     /* shift by 2) and we will have to shift the fraction by the */
365     /* appropriate number of bits to keep the proper magnitude. */
366     shift = (int)
367         (ieee_exp = (int)(((ieee1 >> 16) & 0x7ff0) >> 4) - 1023)
368         & 3;
369     /* the ieee format has an implied "1" immdeiately to the left */
370     /* of the binary point. Show it in here. */
371     xport1 |= 0x00100000;
372     if (shift)
373     {
374         /* set the first half of the ibm number by shifting it left */
375 
376         /* the appropriate number of bits and oring in the bits */
377         /* from the lower half that would have been shifted in (if */
378         /* we could shift a double). The shift count can never */
379         /* exceed 3, so all we care about are the high order 3 */
380         /* bits. We don't want sign extention so make sure it's an */
381         /* unsigned char. We'll shift either5, 6, or 7 places to */
382         /* keep 3, 2, or 1 bits. After that, shift the second half */
383         /* of the number the right number of places. We always get */
384         /* zero fill on left shifts. */
385         xport1 = (xport1 << shift) |
386             ((unsigned char) (((ieee2 >> 24) & 0xE0) >>
387                 (5 + (3 - shift))));
388 
389         xport2 <<= shift;
390     }
391 
392     /* Now set the ibm exponent and the sign of the fraction. The */
393     /* power of 2 ieee exponent must be divided by 4 and made */
394     /* excess 64 (we add 65 here because of the poisition of the */
395     /* fraction bits, essentially 4 positions lower than they */
396     /* should be so we incrment the ibm exponent). */
397 
398     xport1 |=
399 
400         (((ieee_exp >>2) + 65) | ((ieee1 >> 24) & 0x80)) << 24;
401     /* If the ieee exponent is greater than 248 or less than -260, */
402     /* then it cannot fit in the ibm exponent field. Send back the */
403     /* appropriate flag. */
404 
405 doret:
406     if (ieee_exp < -260) {
407         memset(xport,0x00,8);
408     } else if (ieee_exp > 248) {
409         memset(xport+1,0xFF,7);
410 
411         *xport = 0x7F | ((ieee1 >> 24) & 0x80);
412     } else {
413         memreverse(&xport1,sizeof(uint32_t));
414         memcpy(xport,&xport1,sizeof(uint32_t));
415         memreverse(&xport2,sizeof(uint32_t));
416         memcpy(xport+4,&xport2,sizeof(uint32_t));
417     }
418     return;
419 }
420 
421