this repo has no description
1/*
2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*******************************************************************************
23* *
24* File nextafter.c, *
25* Function nextafterd for PowerPC based machines. *
26* *
27* The computation of IEEE-754 nextafter using a floating-point method. *
28* *
29* Copyright � 1991-2001 Apple Computer, Inc. All rights reserved. *
30* *
31* Written by A. Sazegari, started on September 1991. *
32* Modified and ported by Robert A. Murley (ram) for Mac OS X. *
33* *
34* A MathLib v4 file. *
35* *
36* Change History (most recent last): *
37* *
38* October 01 1991: passes all test vectors with no tolerances. *
39* November 11 1991: changed +INF representation from 7FFF8000... *
40* to 7FFF000... same thing for -INF. *
41* November 15 1991: changed classdouble and copysign to CLASSIFY and *
42* COPYSIGN. *
43* November 17 1991: changed classdouble to CLASSEXTENDEDint. passes *
44* the test suite. *
45* February 07 1992: changed COPYSIGN with COPYSIGNold. *
46* September 24 1992: took the "#include support.h" out. *
47* December 03 1992: first rs6000 port. *
48* December 13 1992: added the environmental support. *
49* December 15 1993: created a version without environmental enquires, *
50* added the rounding direction control previously *
51* taken care of by feholdexcept (not really!) and *
52* feupdateenv. changed pack4.h to fp_private.h. *
53* October 12 1994: fixed the environmental problem and tested it. *
54* September 21 1995: corrected the error in the restoration of the *
55* rounding mode. *
56* June 27 2001: ported routine to OSX, replaced calls to fenv.c *
57* with inline routines. This technique caused the *
58* routine to speed up by a factor of three. *
59* July 16 2001: replaced __setflm with FEGETENVD/FESETENVD. *
60* August 28 2001: added description of routine. *
61* September 05 2001: added #ifdef __ppc__. *
62* September 10 2001: added macros to detect PowerPC and correct compiler.*
63* September 10 2001: added more comments. *
64* September 18 2001: added <CoreServices/CoreServices.h> to get <fenv.h>.*
65* October 08 2001: removed <CoreServices/CoreServices.h>. *
66* changed compiler errors to warnings. *
67* November 02 2001: added stub for i386 version of nextafterd. *
68* November 06 2001: commented out warning about Intel architectures. *
69* changed i386 stub to call abort(). *
70* *
71* W A R N I N G: *
72* These routines require a 64-bit double precision IEEE-754 model. *
73* They are written for PowerPC only and are expecting the compiler *
74* to generate the correct sequence of multiply-add fused instructions. *
75* *
76* These routines are not intended for 32-bit Intel architectures. *
77* *
78* A version of gcc higher than 932 is required. *
79* *
80* GCC compiler options: *
81* optimization level 3 (-O3) *
82* -fschedule-insns -finline-functions -funroll-all-loops *
83* *
84*******************************************************************************/
85
86#include "math.h"
87#include "fp_private.h"
88#include "fenv_private.h"
89
90/*******************************************************************************
91********************************************************************************
92* N E X T A F T E R D *
93********************************************************************************
94* *
95* Computes the next representable double value after 'x' in the direction *
96* of 'y'. if x == y then y is returned. *
97* This algorithm uses the smallest positive number with rounding *
98* directions to compute the nextafter of a double number. *
99* *
100* Functions used in this program: __fpclassify. *
101* *
102*******************************************************************************/
103
104static int ___fpclassifyd ( double arg )
105{
106 uint32_t exponent;
107 hexdouble x;
108
109 x.d = arg;
110 __NOOP;
111 __NOOP;
112 __NOOP;
113
114 exponent = x.i.hi & 0x7ff00000;
115 if ( exponent == 0x7ff00000 )
116 {
117 if ( ( ( x.i.hi & 0x000fffff ) | x.i.lo ) == 0 )
118 return FP_INFINITE;
119 else
120 return ( x.i.hi & dQuietNan ) ? FP_QNAN : FP_SNAN;
121 }
122 else if ( exponent != 0)
123 return FP_NORMAL;
124 else
125 {
126 if ( ( ( x.i.hi & 0x000fffff ) | x.i.lo ) == 0 )
127 return FP_ZERO;
128 else
129 return FP_SUBNORMAL;
130 }
131}
132
133static double __nextafter ( double x, double y )
134 {
135 static const hexdouble EPSILON = HEXDOUBLE(0x00000000, 0x00000001);
136 static const hexdouble PosINF = HEXDOUBLE(0x7ff00000, 0x00000000);
137 static const hexdouble NegINF = HEXDOUBLE(0xfff00000, 0x00000000);
138 static const hexdouble PosBig = HEXDOUBLE(0x7fefffff, 0xffffffff);
139 static const hexdouble NegBig = HEXDOUBLE(0xffefffff, 0xffffffff);
140 double arg;
141 hexdouble temp, xsign, ysign;
142 register int newexc;
143 fenv_t envp;
144
145// Save old environment
146 FEGETENVD_GRP(temp.d);
147 envp = temp.i.lo;
148
149// Clear all flags in temp variable and set rounding to nearest
150 temp.i.lo &= (FE_NO_FLAGS & FE_NO_ENABLES & FE_NO_RND);
151 temp.i.lo |= FE_TONEAREST;
152
153 if (unlikely( ( x != x ) || ( y != y ) )) // one of the arguments is a NaN
154 arg = y + x;
155 else if ( x == y ) /* Exit here in case the answer is �INF. */
156 { /* Otherwise unwanted flags will be set. */
157 // Save exceptions, restore environment with saved exceptions
158 newexc = temp.i.lo & FE_ALL_EXCEPT;
159 temp.i.lo = envp;
160 if ((newexc & FE_INVALID) != 0)
161 temp.i.lo |= SET_INVALID;
162 temp.i.lo |= newexc & ( FE_INEXACT | FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW );
163 FESETENVD_GRP(temp.d);
164 if ( ( x == 0.0 ) && ( y == 0.0 )) // (*0, -0)-> -0, (*0, +0)-> +0 i.e. "y"
165 return y;
166 else
167 return x;
168 }
169 else if (unlikely( ( x == PosINF.d ) || ( x == NegINF.d ) ))
170 arg = ( x > 0 ) ? PosBig.d : NegBig.d; // x and/or y is INF
171 else if ( x == 0.0 )
172 {
173 xsign.d = EPSILON.d; // copy y's sign to EPSILON
174 ysign.d = y;
175 __NOOP;
176 __NOOP;
177
178 xsign.i.hi &= 0x7fffffff;
179 xsign.i.hi |= ysign.i.hi & 0x80000000;
180 __NOOP;
181 __NOOP;
182 __NOOP;
183 arg = xsign.d;
184 temp.i.lo |= FE_INEXACT | FE_UNDERFLOW;
185 }
186 else if ( ( ( x < 0.0 ) && ( x < y ) ) || ( ( x > 0.0 ) && ( x > y ) ) )
187 { /* Always clear intermediate spurious inexact. */
188 temp.i.lo = (temp.i.lo & FE_NO_RND) | FE_TOWARDZERO;
189 FESETENVD_GRP(temp.d);
190 arg = ( x < 0.0 ) ? x + EPSILON.d : x - EPSILON.d;
191 temp.i.lo &= ~( FE_INEXACT | FE_UNDERFLOW );
192 if ((temp.i.lo & FE_ALL_EXCEPT) == 0)
193 temp.i.lo &= FE_CLR_FX;
194 }
195 else if ( ( x < 0.0 ) && ( x > y ) )
196 {
197 temp.i.lo = (temp.i.lo & FE_NO_RND) | FE_DOWNWARD;
198 FESETENVD_GRP(temp.d);
199 arg = x - EPSILON.d;
200 temp.i.lo &= ~( FE_INEXACT | FE_UNDERFLOW );
201 if ((temp.i.lo & FE_ALL_EXCEPT) == 0)
202 temp.i.lo &= FE_CLR_FX;
203 }
204 else
205 {
206 temp.i.lo = (temp.i.lo & FE_NO_RND) | FE_UPWARD;
207 FESETENVD_GRP(temp.d);
208 arg = x + EPSILON.d;
209 temp.i.lo &= ~( FE_INEXACT | FE_UNDERFLOW );
210 if ((temp.i.lo & FE_ALL_EXCEPT) == 0)
211 temp.i.lo &= FE_CLR_FX;
212 }
213
214/*******************************************************************************
215* Set the flags according to the menu du jour. *
216*******************************************************************************/
217
218 switch ( ___fpclassifyd ( arg ) )
219 {
220 case FP_ZERO:
221 xsign.d = arg; // copy sign from x to arg
222 ysign.d = x;
223 __NOOP;
224 __NOOP;
225
226 xsign.i.hi &= 0x7fffffff;
227 xsign.i.hi |= ( ysign.i.hi & 0x80000000 );
228 __NOOP;
229 __NOOP;
230 __NOOP;
231
232 arg = xsign.d;
233 /* FALL THROUGH */
234 case FP_SUBNORMAL:
235 temp.i.lo |= FE_INEXACT | FE_UNDERFLOW;
236 break;
237 case FP_INFINITE:
238 temp.i.lo |= FE_INEXACT | FE_OVERFLOW;
239 break;
240 }
241
242/*******************************************************************************
243* Not to worry about the rounding directions. feupdateenv saves the day. *
244* In this version, one must restore the rounding direction. *
245*******************************************************************************/
246
247 newexc = temp.i.lo & FE_ALL_EXCEPT;
248 temp.i.lo = envp;
249 if ((newexc & FE_INVALID) != 0)
250 temp.i.lo |= SET_INVALID;
251 temp.i.lo |= newexc & ( FE_INEXACT | FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW );
252 FESETENVD_GRP(temp.d);
253
254 return arg;
255 }
256
257#if !defined(BUILDING_FOR_CARBONCORE_LEGACY)
258
259double nextafter(double x, double y)
260{
261 return __nextafter( x, y );
262}
263
264#else /* BUILDING_FOR_CARBONCORE_LEGACY */
265/* Legacy nextafterd() API */
266
267double nextafterd(double x, double y)
268{
269 return __nextafter( x, y );
270}
271
272static int ___fpclassifyf ( float x )
273{
274 uint32_t iexp;
275 hexsingle z;
276
277 z.fval = x;
278 __NOOP;
279 __NOOP;
280 __NOOP;
281 iexp = z.lval & 0x7f800000; // isolate float exponent
282
283 if (iexp == 0x7f800000) { // NaN or INF case
284 if ((z.lval & 0x007fffff) == 0)
285 return FP_INFINITE;
286 else if ((z.lval & fQuietNan) != 0)
287 return FP_QNAN;
288 else
289 return FP_SNAN;
290 }
291
292 if (iexp != 0) // normal float
293 return FP_NORMAL;
294
295 if ((z.lval & 0x007fffff) == 0)
296 return FP_ZERO; // zero
297 else
298 return FP_SUBNORMAL; //must be subnormal
299}
300
301float nextafterf ( float x, float y )
302 {
303 static const hexsingle EPSILON = { 0x00000001 };
304 static const hexsingle PosINF = { 0x7f800000 };
305 static const hexsingle NegINF = { 0xff800000 };
306 static const hexsingle PosBig = { 0x7f7fffff };
307 static const hexsingle NegBig = { 0xff7fffff };
308 double arg;
309 hexdouble temp;
310 hexsingle xsign, ysign;
311 register int newexc;
312 fenv_t envp;
313
314// Save old environment
315 FEGETENVD_GRP(temp.d);
316 envp = temp.i.lo;
317
318// Clear all flags in temp variable and set rounding to nearest
319 temp.i.lo &= (FE_NO_FLAGS & FE_NO_ENABLES & FE_NO_RND);
320 temp.i.lo |= FE_TONEAREST;
321
322 if (unlikely( ( x != x ) || ( y != y ) )) // one of the arguments is a NaN
323 arg = y + x;
324 else if ( x == y ) /* Exit here in case the answer is �INF. */
325 { /* Otherwise unwanted flags will be set. */
326 // Save exceptions, restore environment with saved exceptions
327 newexc = temp.i.lo & FE_ALL_EXCEPT;
328 temp.i.lo = envp;
329 if ((newexc & FE_INVALID) != 0)
330 temp.i.lo |= SET_INVALID;
331 temp.i.lo |= newexc & ( FE_INEXACT | FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW );
332 FESETENVD_GRP(temp.d);
333 if ( ( x == 0.0 ) && ( y == 0.0 )) // (*0, -0)-> -0, (*0, +0)-> +0
334 return y;
335 else
336 return x;
337 }
338 else if (unlikely( ( x == PosINF.fval ) || ( x == NegINF.fval ) ))
339 arg = ( x > 0 ) ? PosBig.fval : NegBig.fval; // x and/or y is INF
340 else if ( x == 0.0 )
341 {
342 xsign.fval = EPSILON.fval; // copy y's sign to EPSILON
343 ysign.fval = y;
344 __NOOP;
345 __NOOP;
346
347 xsign.lval &= 0x7fffffff;
348 xsign.lval |= ysign.lval & 0x80000000;
349 __NOOP;
350 __NOOP;
351 __NOOP;
352
353 arg = xsign.fval;
354 temp.i.lo |= FE_INEXACT | FE_UNDERFLOW;
355 }
356 else if ( ( ( x < 0.0 ) && ( x < y ) ) || ( ( x > 0.0 ) && ( x > y ) ) )
357 { /* Always clear intermediate spurious inexact. */
358 temp.i.lo = (temp.i.lo & FE_NO_RND) | FE_TOWARDZERO;
359 FESETENVD_GRP(temp.d);
360 arg = ( x < 0.0 ) ? x + EPSILON.fval : x - EPSILON.fval;
361 temp.i.lo &= ~( FE_INEXACT | FE_UNDERFLOW );
362 if ((temp.i.lo & FE_ALL_EXCEPT) == 0)
363 temp.i.lo &= FE_CLR_FX;
364 }
365 else if ( ( x < 0.0 ) && ( x > y ) )
366 {
367 temp.i.lo = (temp.i.lo & FE_NO_RND) | FE_DOWNWARD;
368 FESETENVD_GRP(temp.d);
369 arg = x - EPSILON.fval;
370 temp.i.lo &= ~( FE_INEXACT | FE_UNDERFLOW );
371 if ((temp.i.lo & FE_ALL_EXCEPT) == 0)
372 temp.i.lo &= FE_CLR_FX;
373 }
374 else
375 {
376 temp.i.lo = (temp.i.lo & FE_NO_RND) | FE_UPWARD;
377 FESETENVD_GRP(temp.d);
378 arg = x + EPSILON.fval;
379 temp.i.lo &= ~( FE_INEXACT | FE_UNDERFLOW );
380 if ((temp.i.lo & FE_ALL_EXCEPT) == 0)
381 temp.i.lo &= FE_CLR_FX;
382 }
383
384/*******************************************************************************
385* Set the flags according to the menu du jour. *
386*******************************************************************************/
387
388 switch ( ___fpclassifyf ( (float) arg ) )
389 {
390 case FP_ZERO:
391 xsign.fval = (float) arg; // copy sign from x to arg
392 ysign.fval = x;
393 __NOOP;
394 __NOOP;
395
396 xsign.lval &= 0x7fffffff;
397 xsign.lval |= ( ysign.lval & 0x80000000 );
398 __NOOP;
399 __NOOP;
400 __NOOP;
401
402 arg = xsign.fval;
403 case FP_SUBNORMAL:
404 temp.i.lo |= FE_INEXACT | FE_UNDERFLOW;
405 break;
406 case FP_INFINITE:
407 temp.i.lo |= FE_INEXACT | FE_OVERFLOW;
408 break;
409 }
410
411/*******************************************************************************
412* Not to worry about the rounding directions. feupdateenv saves the day. *
413* In this version, one must restore the rounding direction. *
414*******************************************************************************/
415
416 newexc = temp.i.lo & FE_ALL_EXCEPT;
417 temp.i.lo = envp;
418 if ((newexc & FE_INVALID) != 0)
419 temp.i.lo |= SET_INVALID;
420 temp.i.lo |= newexc & ( FE_INEXACT | FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW );
421 FESETENVD_GRP(temp.d);
422
423 return (float) arg;
424 }
425
426#endif /* BUILDING_FOR_CARBONCORE_LEGACY */