/* Copyright 1996 by Abacus Research and * Development, Inc. All rights reserved. */ #if !defined (OMIT_RCSID_STRINGS) char ROMlib_rcsid_floatnext[] = "$Id: floatnext.c 63 2004-12-24 18:19:43Z ctm $"; #endif #include "rsys/common.h" #include "SANE.h" #include "rsys/float.h" #include "rsys/floatconv.h" #if defined (NEXT) #define INLINE_MATH #endif /* Subtracts one from the given multi-byte big endian unsigned number. */ static void mp_decrement_big_endian (uint8 *bytes, int num_bytes) { boolean_t borrow_p; uint8 *b; borrow_p = TRUE; for (b = bytes + num_bytes; borrow_p && b > bytes; ) { const uint8 v = *--b; borrow_p = (v == 0); *b = v - 1; } } /* Adds one to the given multi-byte big endian unsigned number. */ static void mp_increment_big_endian (uint8 *bytes, int num_bytes) { boolean_t carry_p; uint8 *b; carry_p = TRUE; for (b = bytes + num_bytes; carry_p && b > bytes; ) { /* Equivalent to "carry_p = !++*--b;" Heh. */ const uint8 v = *--b + 1; carry_p = (v == 0); *b = v; } } /* Replaces x with the nearest number to x in the direction of y. * * This implementation is not exactly right, but it should be good * enough for Executor 2. In particular, it doesn't handle * denormalized numbers well and it doesn't signal exceptions. * * Note that most SANE traps replace the dst operand with the * result. This one replaces the src operand (really!) */ P_SAVED0D1A0A1_3 (PUBLIC pascal trap, void, ROMlib_FnextX, uint8 *, x, uint8 *, y, unsigned short, sel) { CCRElement saved_ccc, saved_ccn, saved_ccv, saved_ccnz, saved_ccx; INTEGER x_class, y_class, x_class_swapped, y_class_swapped; x80_t x80_x; int byte_size; int x_sign, y_sign; boolean_t normalize_x80_p; ieee_t xv, yv; saved_ccnz = cpu_state.ccnz; saved_ccn = cpu_state.ccn; saved_ccc = cpu_state.ccc; saved_ccv = cpu_state.ccv; saved_ccx = cpu_state.ccx; /* Determine the classes of both X and Y. */ ROMlib_Fclassx (x, &x_class_swapped, sel); x_class = CW (x_class_swapped); ROMlib_Fclassx (y, &y_class_swapped, sel); y_class = CW (y_class_swapped); normalize_x80_p = FALSE; /* default, avoid gcc warnings. */ switch (sel & OPCODE_MASK) { case FX_OPERAND: xv = x80_to_ieee ((const x80_t *) x); yv = x80_to_ieee ((const x80_t *) y); normalize_x80_p = ((x[2] & 0x80) != 0); byte_size = 10; break; case FD_OPERAND: xv = f64_to_ieee ((const f64_t *) x); yv = f64_to_ieee ((const f64_t *) y); byte_size = 8; break; case FS_OPERAND: xv = f32_to_ieee ((const f32_t *) x); yv = f32_to_ieee ((const f32_t *) y); byte_size = 4; break; case FI_OPERAND: xv = CW (*(short *)x); yv = CW (*(short *)y); byte_size = 2; break; case FL_OPERAND: xv = CL(*(long *)x); yv = CL(*(long *)y); byte_size = 4; break; case FC_OPERAND: xv = comp_to_ieee ((const comp_t *) x); yv = comp_to_ieee ((const comp_t *) y); byte_size = 8; break; default: gui_abort (); xv = yv = 0; /* avoid gcc warnings */ goto done; } /* If either number is a NaN, the result is a NaN. Technically it * should be a quiet NaN and we should signal if a signaling * NaN made it here, but we don't do that yet. */ if (y_class == SNaN || y_class == QNaN) { memcpy (x, y, byte_size); goto done; } if (x_class == SNaN || x_class == QNaN) goto done; /* Compare x and y. */ ROMlib_Fx2X (x, &x80_x, sel & OPCODE_MASK); ROMlib_Fcmpx (y, &x80_x, sel & OPCODE_MASK); /* If x == y, just return x unchanged. */ if (!cpu_state.ccnz) goto done; /* Fetch the sign of x and y. */ x_sign = x[0] & 0x80; y_sign = y[0] & 0x80; if (x_class == ZeroNum) { memset (x, 0, byte_size); x[0] = y_sign; x[byte_size - 1] |= 1; /* smallest normalized number. */ } else /* x != 0 */ { if (!cpu_state.ccn ^ !x_sign) mp_increment_big_endian (x, byte_size); else { if (normalize_x80_p) x[2] &= 0x7F; /* make sure borrow goes through normalize bit */ mp_decrement_big_endian (x, byte_size); } /* Restore the sign in case of carry. */ x[0] = (x[0] & 0x7F) | x_sign; } /* Restore normalize bit, which may have gotten carried through or * borrowed through, etc. */ if (normalize_x80_p) x[2] |= 0x80; done: #if ERROR_SUPPORTED_P (ERROR_FLOATING_POINT) { ieee_t result; switch (sel & OPCODE_MASK) { case FX_OPERAND: result = x80_to_ieee ((const x80_t *) x); break; case FD_OPERAND: result = f64_to_ieee ((const f64_t *) x); break; case FS_OPERAND: result = f32_to_ieee ((const f32_t *) x); break; case FI_OPERAND: result = CW (*(short *)x); break; case FL_OPERAND: result = CL (*(long *)x); break; case FC_OPERAND: result = comp_to_ieee ((const comp_t *) x); break; default: gui_abort (); result = 0; } warning_floating_point ("nextafter(%.30f, %.30f) == %.30f", (double) xv, (double) yv, (double) result); } #endif /* ERROR_SUPPORTED_P (ERROR_FLOATING_POINT) */ cpu_state.ccnz = saved_ccnz; cpu_state.ccn = saved_ccn; cpu_state.ccc = saved_ccc; cpu_state.ccv = saved_ccv; cpu_state.ccx = saved_ccx; }