Coverage Report

Created: 2025-06-13 06:18

/src/gdal/port/cpl_vax.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  CPL
4
 * Purpose:  Convert between VAX and IEEE floating point formats
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2000, Avenza Systems Inc, http://www.avenza.com/
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_port.h"
14
#include "cpl_vax.h"
15
16
namespace
17
{
18
typedef struct dbl
19
{
20
    // cppcheck-suppress unusedStructMember
21
    GUInt32 hi;
22
    // cppcheck-suppress unusedStructMember
23
    GUInt32 lo;
24
} double64_t;
25
}  // namespace
26
27
/************************************************************************/
28
/*                          CPLVaxToIEEEDouble()                        */
29
/************************************************************************/
30
31
void CPLVaxToIEEEDouble(void *dbl)
32
33
0
{
34
0
    double64_t dt;
35
0
    GUInt32 sign;
36
0
    int exponent;
37
0
    GUInt32 rndbits;
38
39
    /* -------------------------------------------------------------------- */
40
    /*      Arrange the VAX double so that it may be accessed by a          */
41
    /*      double64_t structure, (two GUInt32s).                           */
42
    /* -------------------------------------------------------------------- */
43
0
    {
44
0
        const unsigned char *src = static_cast<const unsigned char *>(dbl);
45
0
        unsigned char dest[8];
46
0
#ifdef CPL_LSB
47
0
        dest[2] = src[0];
48
0
        dest[3] = src[1];
49
0
        dest[0] = src[2];
50
0
        dest[1] = src[3];
51
0
        dest[6] = src[4];
52
0
        dest[7] = src[5];
53
0
        dest[4] = src[6];
54
0
        dest[5] = src[7];
55
#else
56
        dest[1] = src[0];
57
        dest[0] = src[1];
58
        dest[3] = src[2];
59
        dest[2] = src[3];
60
        dest[5] = src[4];
61
        dest[4] = src[5];
62
        dest[7] = src[6];
63
        dest[6] = src[7];
64
#endif
65
0
        memcpy(&dt, dest, 8);
66
0
    }
67
68
    /* -------------------------------------------------------------------- */
69
    /*      Save the sign of the double                                     */
70
    /* -------------------------------------------------------------------- */
71
0
    sign = dt.hi & 0x80000000;
72
73
    /* -------------------------------------------------------------------- */
74
    /*      Adjust the exponent so that we may work with it                 */
75
    /* -------------------------------------------------------------------- */
76
0
    exponent = (dt.hi >> 23) & 0x000000ff;
77
78
0
    if (exponent)
79
0
        exponent = exponent - 129 + 1023;
80
81
    /* -------------------------------------------------------------------- */
82
    /*      Save the bits that we are discarding so we can round properly   */
83
    /* -------------------------------------------------------------------- */
84
0
    rndbits = dt.lo & 0x00000007;
85
86
0
    dt.lo = dt.lo >> 3;
87
0
    dt.lo = (dt.lo & 0x1fffffff) | (dt.hi << 29);
88
89
0
    if (rndbits)
90
0
        dt.lo = dt.lo | 0x00000001;
91
92
    /* -------------------------------------------------------------------- */
93
    /*      Shift the hi-order int over 3 and insert the exponent and sign  */
94
    /* -------------------------------------------------------------------- */
95
0
    dt.hi = dt.hi >> 3;
96
0
    dt.hi = dt.hi & 0x000fffff;
97
0
    dt.hi = dt.hi | (static_cast<GUInt32>(exponent) << 20) | sign;
98
99
0
#ifdef CPL_LSB
100
    /* -------------------------------------------------------------------- */
101
    /*      Change the number to a byte swapped format                      */
102
    /* -------------------------------------------------------------------- */
103
0
    const unsigned char *src = reinterpret_cast<const unsigned char *>(&dt);
104
0
    unsigned char *dest = static_cast<unsigned char *>(dbl);
105
106
0
    memcpy(dest + 0, src + 4, 4);
107
0
    memcpy(dest + 4, src + 0, 4);
108
#else
109
    memcpy(dbl, &dt, 8);
110
#endif
111
0
}
112
113
/************************************************************************/
114
/*                         CPLIEEEToVaxDouble()                         */
115
/************************************************************************/
116
117
void CPLIEEEToVaxDouble(void *dbl)
118
119
0
{
120
0
    double64_t dt;
121
122
0
#ifdef CPL_LSB
123
0
    {
124
0
        const GByte *src = static_cast<const GByte *>(dbl);
125
0
        GByte dest[8];
126
127
0
        dest[0] = src[4];
128
0
        dest[1] = src[5];
129
0
        dest[2] = src[6];
130
0
        dest[3] = src[7];
131
0
        dest[4] = src[0];
132
0
        dest[5] = src[1];
133
0
        dest[6] = src[2];
134
0
        dest[7] = src[3];
135
0
        memcpy(&dt, dest, 8);
136
0
    }
137
#else
138
    memcpy(&dt, dbl, 8);
139
#endif
140
141
0
    GInt32 sign = dt.hi & 0x80000000;
142
0
    GInt32 exponent = dt.hi >> 20;
143
0
    exponent = exponent & 0x000007ff;
144
145
    /* -------------------------------------------------------------------- */
146
    /*      An exponent of zero means a zero value.                         */
147
    /* -------------------------------------------------------------------- */
148
0
    if (exponent)
149
0
        exponent = exponent - 1023 + 129;
150
151
    /* -------------------------------------------------------------------- */
152
    /*      In the case of overflow, return the largest number we can       */
153
    /* -------------------------------------------------------------------- */
154
0
    if (exponent > 255)
155
0
    {
156
0
        GByte dest[8];
157
158
0
        if (sign)
159
0
            dest[1] = 0xff;
160
0
        else
161
0
            dest[1] = 0x7f;
162
163
0
        dest[0] = 0xff;
164
0
        dest[2] = 0xff;
165
0
        dest[3] = 0xff;
166
0
        dest[4] = 0xff;
167
0
        dest[5] = 0xff;
168
0
        dest[6] = 0xff;
169
0
        dest[7] = 0xff;
170
0
        memcpy(dbl, dest, 8);
171
172
0
        return;
173
0
    }
174
175
    /* -------------------------------------------------------------------- */
176
    /*      In the case of of underflow return zero                         */
177
    /* -------------------------------------------------------------------- */
178
0
    else if ((exponent < 0) || (exponent == 0 && sign == 0))
179
0
    {
180
0
        memset(dbl, 0, 8);
181
182
0
        return;
183
0
    }
184
0
    else
185
0
    {
186
        /* --------------------------------------------------------------------
187
         */
188
        /*          Shift the fraction 3 bits left and set the exponent and
189
         * sign*/
190
        /* --------------------------------------------------------------------
191
         */
192
0
        dt.hi = dt.hi << 3;
193
0
        dt.hi = dt.hi | (dt.lo >> 29);
194
0
        dt.hi = dt.hi & 0x007fffff;
195
0
        dt.hi = dt.hi | (exponent << 23) | sign;
196
197
0
        dt.lo = dt.lo << 3;
198
0
    }
199
200
    /* -------------------------------------------------------------------- */
201
    /*      Convert the double back to VAX format                           */
202
    /* -------------------------------------------------------------------- */
203
0
    const GByte *src = reinterpret_cast<GByte *>(&dt);
204
205
0
#ifdef CPL_LSB
206
0
    GByte *dest = static_cast<GByte *>(dbl);
207
0
    memcpy(dest + 2, src + 0, 2);
208
0
    memcpy(dest + 0, src + 2, 2);
209
0
    memcpy(dest + 6, src + 4, 2);
210
0
    memcpy(dest + 4, src + 6, 2);
211
#else
212
    GByte dest[8];
213
    dest[1] = src[0];
214
    dest[0] = src[1];
215
    dest[3] = src[2];
216
    dest[2] = src[3];
217
    dest[5] = src[4];
218
    dest[4] = src[5];
219
    dest[7] = src[6];
220
    dest[6] = src[7];
221
    memcpy(dbl, dest, 8);
222
#endif
223
0
}
224
225
//////////////////////////////////////////////////////////////////////////
226
/// Below code is adapted from Public Domain VICAR project
227
/// https://github.com/nasa/VICAR/blob/master/vos/rtl/source/conv_vax_ieee_r.c
228
//////////////////////////////////////////////////////////////////////////
229
230
static void real_byte_swap(const unsigned char from[4], unsigned char to[4])
231
0
{
232
0
    to[0] = from[1];
233
0
    to[1] = from[0];
234
0
    to[2] = from[3];
235
0
    to[3] = from[2];
236
0
}
237
238
/* Shift x[1]..x[3] right one bit by bytes, don't bother with x[0] */
239
#define SHIFT_RIGHT(x)                                                         \
240
0
    {                                                                          \
241
0
        x[3] = ((x[3] >> 1) & 0x7F) | ((x[2] << 7) & 0x80);                    \
242
0
        x[2] = ((x[2] >> 1) & 0x7F) | ((x[1] << 7) & 0x80);                    \
243
0
        x[1] = (x[1] >> 1) & 0x7F;                                             \
244
0
    }
245
246
/* Shift x[1]..x[3] left one bit by bytes, don't bother with x[0] */
247
#define SHIFT_LEFT(x)                                                          \
248
0
    {                                                                          \
249
0
        x[1] = ((x[1] << 1) & 0xFE) | ((x[2] >> 7) & 0x01);                    \
250
0
        x[2] = ((x[2] << 1) & 0xFE) | ((x[3] >> 7) & 0x01);                    \
251
0
        x[3] = (x[3] << 1) & 0xFE;                                             \
252
0
    }
253
254
/************************************************************************/
255
/* Convert between IEEE and Vax single-precision floating point.        */
256
/* Both formats are represented as:                                     */
257
/* (-1)^s * f * 2^(e-bias)                                              */
258
/* where s is the sign bit, f is the mantissa (see below), e is the     */
259
/* exponent, and bias is the exponent bias (see below).                 */
260
/* There is an assumed leading 1 on the mantissa (except for IEEE       */
261
/* denormalized numbers), but the placement of the binary point varies. */
262
/*                                                                      */
263
/* IEEE format:    seeeeeee efffffff 8*f 8*f                            */
264
/*        where e is exponent with bias of 127 and f is of the          */
265
/*        form 1.fffff...                                               */
266
/* Special cases:                                                       */
267
/*    e=255, f!=0:        NaN (Not a Number)                            */
268
/*    e=255, f=0:        Infinity (+/- depending on s)                  */
269
/*    e=0, f!=0:        Denormalized numbers, of the form               */
270
/*                (-1)^s * (0.ffff) * 2^(-126)                          */
271
/*    e=0, f=0:        Zero (can be +/-)                                */
272
/*                                                                      */
273
/* VAX format:    seeeeeee efffffff 8*f 8*f                             */
274
/*        where e is exponent with bias of 128 and f is of the          */
275
/*        form .1fffff...                                               */
276
/* Byte swapping: Note that the above format is the logical format,     */
277
/*        which can be represented as bytes SE1 E2F1 F2 F3.             */
278
/*        The actual order in memory is E2F1 SE1 F3 F2 (which is        */
279
/*        two half-word swaps, NOT a full-word swap).                   */
280
/* Special cases:                                                       */
281
/*    e=0, s=0:        Zero (no +/-)                                    */
282
/*    e=0, s=1:        Invalid, causes Reserved Operand error           */
283
/*                                                                      */
284
/* The same code works on all byte-order machines because only byte     */
285
/* operations are performed.  It could perhaps be done more efficiently */
286
/* on a longword basis, but then the code would be byte-order dependent.*/
287
/* MAKE SURE any mods will work on either byte order!!!                 */
288
/************************************************************************/
289
290
/************************************************************************/
291
/* This routine will convert VAX F floating point values to IEEE        */
292
/* single precision floating point.                                     */
293
/************************************************************************/
294
295
static void vax_ieee_r(const unsigned char *from, unsigned char *ieee)
296
0
{
297
0
    unsigned char vaxf[4];
298
0
    unsigned char exp;
299
300
0
    real_byte_swap(from, vaxf); /* Put bytes in rational order */
301
0
    memcpy(ieee, vaxf, 4);      /* Since most bits are the same */
302
303
0
    exp = ((vaxf[0] << 1) & 0xFE) | ((vaxf[1] >> 7) & 0x01);
304
305
0
    if (exp == 0)
306
0
    { /* Zero or invalid pattern */
307
0
        if (vaxf[0] & 0x80)
308
0
        {                   /* Sign bit set, which is illegal for VAX */
309
0
            ieee[0] = 0x7F; /* IEEE NaN */
310
0
            ieee[1] = 0xFF;
311
0
            ieee[2] = 0xFF;
312
0
            ieee[3] = 0xFF;
313
0
        }
314
0
        else
315
0
        { /* Zero */
316
0
            ieee[0] = ieee[1] = ieee[2] = ieee[3] = 0;
317
0
        }
318
0
    }
319
320
0
    else if (exp >= 3)
321
0
    { /* Normal case */
322
0
        exp -= 2;
323
0
        ieee[0] =
324
0
            (vaxf[0] & 0x80) | ((exp >> 1) & 0x7F); /* remake sign + exponent */
325
0
    } /* Low bit of exp can't change, so don't bother w/it */
326
327
0
    else if (exp == 2)
328
0
    {                                      /* Denormalize the number */
329
0
        SHIFT_RIGHT(ieee);                 /* Which means shift right 1, */
330
0
        ieee[1] = (ieee[1] & 0x3F) | 0x40; /* Add suppressed most signif bit, */
331
0
        ieee[0] = vaxf[0] & 0x80; /* and set exponent to 0 (preserving sign) */
332
0
    }
333
334
0
    else
335
0
    {                      /* Exp==1, denormalize again */
336
0
        SHIFT_RIGHT(ieee); /* Like above but shift by 2 */
337
0
        SHIFT_RIGHT(ieee);
338
0
        ieee[1] = (ieee[1] & 0x1F) | 0x20;
339
0
        ieee[0] = vaxf[0] & 0x80;
340
0
    }
341
342
0
#ifdef CPL_LSB
343
0
    CPL_SWAP32PTR(ieee);
344
0
#endif
345
0
}
346
347
/************************************************************************/
348
/* This routine will convert IEEE single precision floating point       */
349
/* values to VAX F floating point.                                      */
350
/************************************************************************/
351
352
static void ieee_vax_r(unsigned char *ieee, unsigned char *to)
353
0
{
354
0
    unsigned char vaxf[4];
355
0
    unsigned char exp;
356
357
0
#ifdef CPL_LSB
358
0
    CPL_SWAP32PTR(ieee);
359
0
#endif
360
361
0
    memcpy(vaxf, ieee, 4); /* Since most bits are the same */
362
363
0
    exp = ((ieee[0] << 1) & 0xFE) | ((ieee[1] >> 7) & 0x01);
364
365
    /* Exponent 255 means NaN or Infinity, exponent 254 is too large for */
366
    /* VAX notation.  In either case, set to sign * highest possible number */
367
368
0
    if (exp == 255 || exp == 254)
369
0
    { /* Infinity or NaN or too big */
370
0
        vaxf[0] = 0x7F | (ieee[0] & 0x80);
371
0
        vaxf[1] = 0xFF;
372
0
        vaxf[2] = 0xFF;
373
0
        vaxf[3] = 0xFF;
374
0
    }
375
376
0
    else if (exp != 0)
377
0
    { /* Normal case */
378
0
        exp += 2;
379
0
        vaxf[0] =
380
0
            (ieee[0] & 0x80) | ((exp >> 1) & 0x7F); /* remake sign + exponent */
381
0
    } /* Low bit of exp can't change, so don't bother w/it */
382
383
0
    else
384
0
    { /* exp == 0, zero or denormalized number */
385
0
        if (ieee[1] == 0 && ieee[2] == 0 && ieee[3] == 0)
386
0
        { /* +/- 0 */
387
0
            vaxf[0] = vaxf[1] = vaxf[2] = vaxf[3] = 0;
388
0
        }
389
0
        else
390
0
        { /* denormalized number */
391
0
            if (ieee[1] & 0x40)
392
0
            {                                      /* hi bit set (0.1ffff) */
393
0
                SHIFT_LEFT(vaxf);                  /* Renormalize */
394
0
                vaxf[1] = vaxf[1] & 0x7F;          /* Set vax exponent to 2 */
395
0
                vaxf[0] = (ieee[0] & 0x80) | 0x01; /* sign, exponent==2 */
396
0
            }
397
0
            else if (ieee[1] & 0x20)
398
0
            {                     /* next bit set (0.01ffff) */
399
0
                SHIFT_LEFT(vaxf); /* Renormalize */
400
0
                SHIFT_LEFT(vaxf);
401
0
                vaxf[1] = vaxf[1] | 0x80; /* Set vax exponent to 1 */
402
0
                vaxf[0] = ieee[0] & 0x80; /* sign, exponent==1 */
403
0
            }
404
0
            else
405
0
            { /* Number too small for VAX */
406
0
                vaxf[0] = vaxf[1] = vaxf[2] = vaxf[3] = 0; /* so set to 0 */
407
0
            }
408
0
        }
409
0
    }
410
411
0
    real_byte_swap(vaxf, to); /* Put bytes in weird VAX order */
412
0
}
413
414
void CPLVaxToIEEEFloat(void *f)
415
0
{
416
0
    unsigned char res[4];
417
0
    vax_ieee_r(static_cast<const unsigned char *>(f), res);
418
0
    memcpy(f, res, 4);
419
0
}
420
421
void CPLIEEEToVaxFloat(void *f)
422
0
{
423
0
    unsigned char res[4];
424
0
    ieee_vax_r(static_cast<unsigned char *>(f), res);
425
0
    memcpy(f, res, 4);
426
0
}