/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 | } |