Coverage Report

Created: 2026-09-01 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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