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