/src/readstat/src/sas/ieee.c
Line | Count | Source |
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 | 114k | #define FLOATREP get_native() |
19 | | int get_native(void); |
20 | | #endif |
21 | | |
22 | 264k | void memreverse(void *intp_void, int l) { |
23 | 264k | if (!machine_is_little_endian()) |
24 | 0 | return; |
25 | | |
26 | 264k | int i,j; |
27 | 264k | char save; |
28 | 264k | char *intp = (char *)intp_void; |
29 | | |
30 | 264k | j = l/2; |
31 | 793k | for (i=0;i<j;i++) { |
32 | 528k | save = intp[i]; |
33 | 528k | intp[i] = intp[l-i-1]; |
34 | 528k | intp[l-i-1] = save; |
35 | 528k | } |
36 | 264k | } |
37 | | |
38 | | int cnxptiee(const void *from_bytes, int fromtype, void *to_bytes, int totype) |
39 | 114k | { |
40 | 114k | unsigned char *from = (unsigned char *)from_bytes; |
41 | 114k | unsigned char *to = (unsigned char *)to_bytes; |
42 | 114k | unsigned char temp[8]; |
43 | 114k | int i; |
44 | | |
45 | 114k | if (fromtype == CN_TYPE_NATIVE) { |
46 | 0 | fromtype = FLOATREP; |
47 | 0 | } |
48 | 114k | switch(fromtype) { |
49 | 0 | case CN_TYPE_IEEEL : |
50 | 0 | if (totype == CN_TYPE_IEEEL) |
51 | 0 | break; |
52 | 0 | for (i=7;i>=0;i--) { |
53 | 0 | temp[7-i] = from[i]; |
54 | 0 | } |
55 | 0 | from = temp; |
56 | 0 | fromtype = CN_TYPE_IEEEB; |
57 | | /* Break intentionally omitted. */ |
58 | 0 | case CN_TYPE_IEEEB : |
59 | | /* Break intentionally omitted. */ |
60 | 114k | case CN_TYPE_XPORT : |
61 | 114k | break; |
62 | 0 | default: |
63 | 0 | return(-1); |
64 | 114k | } |
65 | 114k | if (totype == CN_TYPE_NATIVE) { |
66 | 114k | totype = FLOATREP; |
67 | 114k | } |
68 | 114k | switch(totype) { |
69 | 0 | case CN_TYPE_XPORT : |
70 | 0 | case CN_TYPE_IEEEB : |
71 | 114k | case CN_TYPE_IEEEL : |
72 | 114k | break; |
73 | 0 | default: |
74 | 0 | return(-2); |
75 | 114k | } |
76 | 114k | if (fromtype == totype) { |
77 | 0 | memcpy(to,from,8); |
78 | 0 | return(0); |
79 | 0 | } |
80 | 114k | switch(fromtype) { |
81 | 0 | case CN_TYPE_IEEEB : |
82 | 0 | if (totype == CN_TYPE_XPORT) |
83 | 0 | ieee2xpt(from,to); |
84 | 0 | else memcpy(to,from,8); |
85 | 0 | break; |
86 | 114k | case CN_TYPE_XPORT : |
87 | 114k | xpt2ieee(from,to); |
88 | 114k | break; |
89 | 114k | } |
90 | 114k | if (totype == CN_TYPE_IEEEL) { |
91 | 114k | memcpy(temp,to,8); |
92 | 1.02M | for (i=7;i>=0;i--) { |
93 | 915k | to[7-i] = temp[i]; |
94 | 915k | } |
95 | 114k | } |
96 | 114k | return(0); |
97 | 114k | } |
98 | | |
99 | 114k | int get_native(void) { |
100 | 114k | static unsigned char float_reps[][8] = { |
101 | 114k | {0x41,0x10,0x00,0x00,0x00,0x00,0x00,0x00}, |
102 | 114k | {0x3f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00}, |
103 | 114k | {0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x3f} |
104 | 114k | }; |
105 | | |
106 | 114k | static double one = 1.00; |
107 | | |
108 | 114k | int i,j; |
109 | 114k | j = sizeof(float_reps)/8; |
110 | 343k | for (i=0;i<j;i++) { |
111 | 343k | if (memcmp(&one,float_reps+i,8) == 0) |
112 | 114k | return(i+1); |
113 | 343k | } |
114 | 0 | return(-1); |
115 | 114k | } |
116 | | |
117 | 114k | void xpt2ieee(unsigned char *xport, unsigned char *ieee) { |
118 | 114k | char temp[8]; |
119 | 114k | register int shift; |
120 | 114k | register int nib; |
121 | 114k | uint32_t ieee1,ieee2; |
122 | 114k | uint32_t xport1 = 0; |
123 | 114k | uint32_t xport2 = 0; |
124 | | |
125 | 114k | memcpy(temp,xport,8); |
126 | 114k | memset(ieee,0,8); |
127 | | |
128 | 114k | if (*temp && memcmp(temp+1,ieee,7) == 0) { |
129 | 270 | ieee[0] = ieee[1] = 0xff; |
130 | 270 | ieee[2] = ~(*temp); |
131 | 270 | return; |
132 | 270 | } |
133 | | |
134 | 114k | memcpy(&xport1,temp,sizeof(uint32_t)); |
135 | 114k | memreverse(&xport1,sizeof(uint32_t)); |
136 | 114k | memcpy(&xport2,temp+4,sizeof(uint32_t)); |
137 | 114k | 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 | 114k | if ((xport1 & 0x7fffffff) == 0x7fffffff && xport2 == 0xffffffff) { |
185 | 194 | ieee1 = (xport1 & 0x80000000) | 0x7ff00000; |
186 | 194 | ieee2 = 0; |
187 | 194 | goto doret; |
188 | 194 | } |
189 | | |
190 | | /* Get the first half of the ibm number without the exponent */ |
191 | | /* into the ieee number */ |
192 | 113k | 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 | 113k | if ((!(ieee2 = xport2)) && !xport1) |
198 | 96.1k | 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 | 17.8k | if ((nib = (int)xport1) & 0x00800000) { |
207 | 10.1k | shift = 3; |
208 | 10.1k | } else if (nib & 0x00400000) { |
209 | 1.87k | shift = 2; |
210 | 5.87k | } else if (nib & 0x00200000) { |
211 | 4.89k | shift = 1; |
212 | 4.89k | } else { |
213 | 982 | shift = 0; |
214 | 982 | } |
215 | | |
216 | 17.8k | 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 | 16.8k | ieee1 >>= shift; |
225 | 16.8k | ieee2 = (xport2 >> shift) | |
226 | 16.8k | ((xport1 & 0x00000007) << (29 + (3 - shift))); |
227 | 16.8k | } |
228 | | |
229 | | /* clear the 1 bit to the left of the binary point */ |
230 | 17.8k | 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 | 17.8k | ieee1 |= |
239 | 17.8k | (((((int32_t)(*temp & 0x7f) - 65) * 4) + shift + 1023) << 20) | |
240 | 17.8k | (xport1 & 0x80000000); |
241 | | |
242 | 18.0k | doret: |
243 | 18.0k | memreverse(&ieee1,sizeof(uint32_t)); |
244 | 18.0k | memcpy(ieee,&ieee1,sizeof(uint32_t)); |
245 | 18.0k | memreverse(&ieee2,sizeof(uint32_t)); |
246 | 18.0k | memcpy(ieee+4,&ieee2,sizeof(uint32_t)); |
247 | 18.0k | return; |
248 | 17.8k | } |
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 | | |
258 | 0 | void ieee2xpt(unsigned char *ieee, unsigned char *xport) { |
259 | 0 | register int shift; |
260 | 0 | unsigned char misschar; |
261 | 0 | int ieee_exp; |
262 | 0 | uint32_t xport1,xport2; |
263 | 0 | uint32_t ieee1 = 0; |
264 | 0 | uint32_t ieee2 = 0; |
265 | |
|
266 | 0 | char ieee8[8]; |
267 | |
|
268 | 0 | memcpy(ieee8,ieee,8); |
269 | | |
270 | | /*------get 2 longs for shifting------------------------------*/ |
271 | 0 | memcpy(&ieee1,ieee8,sizeof(uint32_t)); |
272 | 0 | memreverse(&ieee1,sizeof(uint32_t)); |
273 | 0 | memcpy(&ieee2,ieee8+4,sizeof(uint32_t)); |
274 | 0 | memreverse(&ieee2,sizeof(uint32_t)); |
275 | |
|
276 | 0 | memset(xport,0,8); |
277 | | |
278 | | /*-----if IEEE value is missing (1st 2 bytes are FFFF)-----*/ |
279 | 0 | if (*ieee8 == (char)0xff && ieee8[1] == (char)0xff) { |
280 | 0 | misschar = ~ieee8[2]; |
281 | 0 | *xport = (misschar == 0xD2) ? 0x6D : misschar; |
282 | 0 | return; |
283 | 0 | } |
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 | 0 | 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 | 0 | if ((!(xport2 = ieee2)) && !ieee1) { |
356 | 0 | ieee_exp = 0; |
357 | 0 | goto doret; |
358 | 0 | } |
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 | 0 | shift = (int) |
367 | 0 | (ieee_exp = (int)(((ieee1 >> 16) & 0x7ff0) >> 4) - 1023) |
368 | 0 | & 3; |
369 | | /* the ieee format has an implied "1" immediately to the left */ |
370 | | /* of the binary point. Show it in here. */ |
371 | 0 | xport1 |= 0x00100000; |
372 | 0 | if (shift) |
373 | 0 | { |
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 extension 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 | 0 | xport1 = (xport1 << shift) | |
386 | 0 | ((unsigned char) (((ieee2 >> 24) & 0xE0) >> |
387 | 0 | (5 + (3 - shift)))); |
388 | |
|
389 | 0 | xport2 <<= shift; |
390 | 0 | } |
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 position of the */ |
395 | | /* fraction bits, essentially 4 positions lower than they */ |
396 | | /* should be so we increment the ibm exponent). */ |
397 | |
|
398 | 0 | xport1 |= |
399 | |
|
400 | 0 | (((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 | 0 | doret: |
406 | 0 | if (ieee_exp < -260) { |
407 | 0 | memset(xport,0x00,8); |
408 | 0 | } else if (ieee_exp > 248) { |
409 | 0 | memset(xport+1,0xFF,7); |
410 | |
|
411 | 0 | *xport = 0x7F | ((ieee1 >> 24) & 0x80); |
412 | 0 | } else { |
413 | 0 | memreverse(&xport1,sizeof(uint32_t)); |
414 | 0 | memcpy(xport,&xport1,sizeof(uint32_t)); |
415 | 0 | memreverse(&xport2,sizeof(uint32_t)); |
416 | 0 | memcpy(xport+4,&xport2,sizeof(uint32_t)); |
417 | 0 | } |
418 | 0 | return; |
419 | 0 | } |
420 | | |