msvcrt: Use the thiscall type on appropriate entry points.
[wine] / dlls / msvcrt / math.c
1 /*
2  * msvcrt.dll math functions
3  *
4  * Copyright 2000 Jon Griffiths
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 #include "config.h"
21
22 #include <stdio.h>
23 #define __USE_ISOC9X 1
24 #define __USE_ISOC99 1
25 #include <math.h>
26 #ifdef HAVE_IEEEFP_H
27 #include <ieeefp.h>
28 #endif
29
30 #include "msvcrt.h"
31
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
35
36 #ifndef HAVE_FINITE
37 #ifndef finite /* Could be a macro */
38 #ifdef isfinite
39 #define finite(x) isfinite(x)
40 #else
41 #define finite(x) (!isnan(x)) /* At least catch some cases */
42 #endif
43 #endif
44 #endif
45
46 #ifndef signbit
47 #define signbit(x) 0
48 #endif
49
50 typedef int (CDECL *MSVCRT_matherr_func)(struct MSVCRT__exception *);
51
52 static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL;
53
54 #ifdef __x86_64__
55
56 /*********************************************************************
57  *      MSVCRT_acosf (MSVCRT.@)
58  */
59 float CDECL MSVCRT_acosf( float x )
60 {
61   if (x < -1.0 || x > 1.0 || !finitef(x)) *MSVCRT__errno() = MSVCRT_EDOM;
62   /* glibc implements acos() as the FPU equivalent of atan2(sqrt(1 - x ^ 2), x).
63    * asin() uses a similar construction. This is bad because as x gets nearer to
64    * 1 the error in the expression "1 - x^2" can get relatively large due to
65    * cancellation. The sqrt() makes things worse. A safer way to calculate
66    * acos() is to use atan2(sqrt((1 - x) * (1 + x)), x). */
67   return atan2f(sqrtf((1 - x) * (1 + x)), x);
68 }
69
70 /*********************************************************************
71  *      MSVCRT_asinf (MSVCRT.@)
72  */
73 float CDECL MSVCRT_asinf( float x )
74 {
75   if (x < -1.0 || x > 1.0 || !finitef(x)) *MSVCRT__errno() = MSVCRT_EDOM;
76   return atan2f(x, sqrtf((1 - x) * (1 + x)));
77 }
78
79 /*********************************************************************
80  *      MSVCRT_atanf (MSVCRT.@)
81  */
82 float CDECL MSVCRT_atanf( float x )
83 {
84   if (!finitef(x)) *MSVCRT__errno() = MSVCRT_EDOM;
85   return atanf(x);
86 }
87
88 /*********************************************************************
89  *              MSVCRT_atan2f (MSVCRT.@)
90  */
91 float CDECL MSVCRT_atan2f( float x, float y )
92 {
93   if (!finitef(x)) *MSVCRT__errno() = MSVCRT_EDOM;
94   return atan2f(x,y);
95 }
96
97 /*********************************************************************
98  *      MSVCRT_cosf (MSVCRT.@)
99  */
100 float CDECL MSVCRT_cosf( float x )
101 {
102   if (!finitef(x)) *MSVCRT__errno() = MSVCRT_EDOM;
103   return cosf(x);
104 }
105
106 /*********************************************************************
107  *      MSVCRT_coshf (MSVCRT.@)
108  */
109 float CDECL MSVCRT_coshf( float x )
110 {
111   if (!finitef(x)) *MSVCRT__errno() = MSVCRT_EDOM;
112   return coshf(x);
113 }
114
115 /*********************************************************************
116  *      MSVCRT_expf (MSVCRT.@)
117  */
118 float CDECL MSVCRT_expf( float x )
119 {
120   if (!finitef(x)) *MSVCRT__errno() = MSVCRT_EDOM;
121   return expf(x);
122 }
123
124 /*********************************************************************
125  *      MSVCRT_fmodf (MSVCRT.@)
126  */
127 float CDECL MSVCRT_fmodf( float x, float y )
128 {
129   if (!finitef(x) || !finitef(y)) *MSVCRT__errno() = MSVCRT_EDOM;
130   return fmodf(x,y);
131 }
132
133 /*********************************************************************
134  *      MSVCRT_logf (MSVCRT.@)
135  */
136 float CDECL MSVCRT_logf( float x)
137 {
138   if (x < 0.0 || !finitef(x)) *MSVCRT__errno() = MSVCRT_EDOM;
139   if (x == 0.0) *MSVCRT__errno() = MSVCRT_ERANGE;
140   return logf(x);
141 }
142
143 /*********************************************************************
144  *      MSVCRT_log10f (MSVCRT.@)
145  */
146 float CDECL MSVCRT_log10f( float x )
147 {
148   if (x < 0.0 || !finitef(x)) *MSVCRT__errno() = MSVCRT_EDOM;
149   if (x == 0.0) *MSVCRT__errno() = MSVCRT_ERANGE;
150   return log10f(x);
151 }
152
153 /*********************************************************************
154  *      MSVCRT_powf (MSVCRT.@)
155  */
156 float CDECL MSVCRT_powf( float x, float y )
157 {
158   /* FIXME: If x < 0 and y is not integral, set EDOM */
159   float z = powf(x,y);
160   if (!finitef(z)) *MSVCRT__errno() = MSVCRT_EDOM;
161   return z;
162 }
163
164 /*********************************************************************
165  *      MSVCRT_sinf (MSVCRT.@)
166  */
167 float CDECL MSVCRT_sinf( float x )
168 {
169   if (!finitef(x)) *MSVCRT__errno() = MSVCRT_EDOM;
170   return sinf(x);
171 }
172
173 /*********************************************************************
174  *      MSVCRT_sinhf (MSVCRT.@)
175  */
176 float CDECL MSVCRT_sinhf( float x )
177 {
178   if (!finitef(x)) *MSVCRT__errno() = MSVCRT_EDOM;
179   return sinhf(x);
180 }
181
182 /*********************************************************************
183  *      MSVCRT_sqrtf (MSVCRT.@)
184  */
185 float CDECL MSVCRT_sqrtf( float x )
186 {
187   if (x < 0.0 || !finitef(x)) *MSVCRT__errno() = MSVCRT_EDOM;
188   return sqrtf(x);
189 }
190
191 /*********************************************************************
192  *      MSVCRT_tanf (MSVCRT.@)
193  */
194 float CDECL MSVCRT_tanf( float x )
195 {
196   if (!finitef(x)) *MSVCRT__errno() = MSVCRT_EDOM;
197   return tanf(x);
198 }
199
200 /*********************************************************************
201  *      MSVCRT_tanhf (MSVCRT.@)
202  */
203 float CDECL MSVCRT_tanhf( float x )
204 {
205   if (!finitef(x)) *MSVCRT__errno() = MSVCRT_EDOM;
206   return tanhf(x);
207 }
208
209 /*********************************************************************
210  *      ceilf (MSVCRT.@)
211  */
212 float CDECL MSVCRT_ceilf( float x )
213 {
214   return ceilf(x);
215 }
216
217 /*********************************************************************
218  *      floorf (MSVCRT.@)
219  */
220 float CDECL MSVCRT_floorf( float x )
221 {
222   return floorf(x);
223 }
224
225 /*********************************************************************
226  *      frexpf (MSVCRT.@)
227  */
228 float CDECL MSVCRT_frexpf( float x, int *exp )
229 {
230   return frexpf( x, exp );
231 }
232
233 /*********************************************************************
234  *      _scalbf (MSVCRT.@)
235  */
236 float CDECL MSVCRT__scalbf(float num, MSVCRT_long power)
237 {
238   if (!finitef(num)) *MSVCRT__errno() = MSVCRT_EDOM;
239   return ldexpf(num, power);
240 }
241
242 /*********************************************************************
243  *      modff (MSVCRT.@)
244  */
245 double CDECL MSVCRT_modff( float x, float *iptr )
246 {
247   return modff( x, iptr );
248 }
249
250 #endif
251
252 /*********************************************************************
253  *              MSVCRT_acos (MSVCRT.@)
254  */
255 double CDECL MSVCRT_acos( double x )
256 {
257   if (x < -1.0 || x > 1.0 || !finite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
258   /* glibc implements acos() as the FPU equivalent of atan2(sqrt(1 - x ^ 2), x).
259    * asin() uses a similar construction. This is bad because as x gets nearer to
260    * 1 the error in the expression "1 - x^2" can get relatively large due to
261    * cancellation. The sqrt() makes things worse. A safer way to calculate
262    * acos() is to use atan2(sqrt((1 - x) * (1 + x)), x). */
263   return atan2(sqrt((1 - x) * (1 + x)), x);
264 }
265
266 /*********************************************************************
267  *              MSVCRT_asin (MSVCRT.@)
268  */
269 double CDECL MSVCRT_asin( double x )
270 {
271   if (x < -1.0 || x > 1.0 || !finite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
272   return atan2(x, sqrt((1 - x) * (1 + x)));
273 }
274
275 /*********************************************************************
276  *              MSVCRT_atan (MSVCRT.@)
277  */
278 double CDECL MSVCRT_atan( double x )
279 {
280   if (!finite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
281   return atan(x);
282 }
283
284 /*********************************************************************
285  *              MSVCRT_atan2 (MSVCRT.@)
286  */
287 double CDECL MSVCRT_atan2( double x, double y )
288 {
289   if (!finite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
290   return atan2(x,y);
291 }
292
293 /*********************************************************************
294  *              MSVCRT_cos (MSVCRT.@)
295  */
296 double CDECL MSVCRT_cos( double x )
297 {
298   if (!finite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
299   return cos(x);
300 }
301
302 /*********************************************************************
303  *              MSVCRT_cosh (MSVCRT.@)
304  */
305 double CDECL MSVCRT_cosh( double x )
306 {
307   if (!finite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
308   return cosh(x);
309 }
310
311 /*********************************************************************
312  *              MSVCRT_exp (MSVCRT.@)
313  */
314 double CDECL MSVCRT_exp( double x )
315 {
316   if (!finite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
317   return exp(x);
318 }
319
320 /*********************************************************************
321  *              MSVCRT_fmod (MSVCRT.@)
322  */
323 double CDECL MSVCRT_fmod( double x, double y )
324 {
325   if (!finite(x) || !finite(y)) *MSVCRT__errno() = MSVCRT_EDOM;
326   return fmod(x,y);
327 }
328
329 /*********************************************************************
330  *              MSVCRT_log (MSVCRT.@)
331  */
332 double CDECL MSVCRT_log( double x)
333 {
334   if (x < 0.0 || !finite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
335   if (x == 0.0) *MSVCRT__errno() = MSVCRT_ERANGE;
336   return log(x);
337 }
338
339 /*********************************************************************
340  *              MSVCRT_log10 (MSVCRT.@)
341  */
342 double CDECL MSVCRT_log10( double x )
343 {
344   if (x < 0.0 || !finite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
345   if (x == 0.0) *MSVCRT__errno() = MSVCRT_ERANGE;
346   return log10(x);
347 }
348
349 /*********************************************************************
350  *              MSVCRT_pow (MSVCRT.@)
351  */
352 double CDECL MSVCRT_pow( double x, double y )
353 {
354   /* FIXME: If x < 0 and y is not integral, set EDOM */
355   double z = pow(x,y);
356   if (!finite(z)) *MSVCRT__errno() = MSVCRT_EDOM;
357   return z;
358 }
359
360 /*********************************************************************
361  *              MSVCRT_sin (MSVCRT.@)
362  */
363 double CDECL MSVCRT_sin( double x )
364 {
365   if (!finite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
366   return sin(x);
367 }
368
369 /*********************************************************************
370  *              MSVCRT_sinh (MSVCRT.@)
371  */
372 double CDECL MSVCRT_sinh( double x )
373 {
374   if (!finite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
375   return sinh(x);
376 }
377
378 /*********************************************************************
379  *              MSVCRT_sqrt (MSVCRT.@)
380  */
381 double CDECL MSVCRT_sqrt( double x )
382 {
383   if (x < 0.0 || !finite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
384   return sqrt(x);
385 }
386
387 /*********************************************************************
388  *              MSVCRT_tan (MSVCRT.@)
389  */
390 double CDECL MSVCRT_tan( double x )
391 {
392   if (!finite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
393   return tan(x);
394 }
395
396 /*********************************************************************
397  *              MSVCRT_tanh (MSVCRT.@)
398  */
399 double CDECL MSVCRT_tanh( double x )
400 {
401   if (!finite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
402   return tanh(x);
403 }
404
405
406 #if defined(__GNUC__) && defined(__i386__)
407
408 #define FPU_DOUBLE(var) double var; \
409   __asm__ __volatile__( "fstpl %0;fwait" : "=m" (var) : )
410 #define FPU_DOUBLES(var1,var2) double var1,var2; \
411   __asm__ __volatile__( "fstpl %0;fwait" : "=m" (var2) : ); \
412   __asm__ __volatile__( "fstpl %0;fwait" : "=m" (var1) : )
413
414 /*********************************************************************
415  *              _CIacos (MSVCRT.@)
416  */
417 double CDECL _CIacos(void)
418 {
419   FPU_DOUBLE(x);
420   return MSVCRT_acos(x);
421 }
422
423 /*********************************************************************
424  *              _CIasin (MSVCRT.@)
425  */
426 double CDECL _CIasin(void)
427 {
428   FPU_DOUBLE(x);
429   return MSVCRT_asin(x);
430 }
431
432 /*********************************************************************
433  *              _CIatan (MSVCRT.@)
434  */
435 double CDECL _CIatan(void)
436 {
437   FPU_DOUBLE(x);
438   return MSVCRT_atan(x);
439 }
440
441 /*********************************************************************
442  *              _CIatan2 (MSVCRT.@)
443  */
444 double CDECL _CIatan2(void)
445 {
446   FPU_DOUBLES(x,y);
447   return MSVCRT_atan2(x,y);
448 }
449
450 /*********************************************************************
451  *              _CIcos (MSVCRT.@)
452  */
453 double CDECL _CIcos(void)
454 {
455   FPU_DOUBLE(x);
456   return MSVCRT_cos(x);
457 }
458
459 /*********************************************************************
460  *              _CIcosh (MSVCRT.@)
461  */
462 double CDECL _CIcosh(void)
463 {
464   FPU_DOUBLE(x);
465   return MSVCRT_cosh(x);
466 }
467
468 /*********************************************************************
469  *              _CIexp (MSVCRT.@)
470  */
471 double CDECL _CIexp(void)
472 {
473   FPU_DOUBLE(x);
474   return MSVCRT_exp(x);
475 }
476
477 /*********************************************************************
478  *              _CIfmod (MSVCRT.@)
479  */
480 double CDECL _CIfmod(void)
481 {
482   FPU_DOUBLES(x,y);
483   return MSVCRT_fmod(x,y);
484 }
485
486 /*********************************************************************
487  *              _CIlog (MSVCRT.@)
488  */
489 double CDECL _CIlog(void)
490 {
491   FPU_DOUBLE(x);
492   return MSVCRT_log(x);
493 }
494
495 /*********************************************************************
496  *              _CIlog10 (MSVCRT.@)
497  */
498 double CDECL _CIlog10(void)
499 {
500   FPU_DOUBLE(x);
501   return MSVCRT_log10(x);
502 }
503
504 /*********************************************************************
505  *              _CIpow (MSVCRT.@)
506  */
507 double CDECL _CIpow(void)
508 {
509   FPU_DOUBLES(x,y);
510   return MSVCRT_pow(x,y);
511 }
512
513 /*********************************************************************
514  *              _CIsin (MSVCRT.@)
515  */
516 double CDECL _CIsin(void)
517 {
518   FPU_DOUBLE(x);
519   return MSVCRT_sin(x);
520 }
521
522 /*********************************************************************
523  *              _CIsinh (MSVCRT.@)
524  */
525 double CDECL _CIsinh(void)
526 {
527   FPU_DOUBLE(x);
528   return MSVCRT_sinh(x);
529 }
530
531 /*********************************************************************
532  *              _CIsqrt (MSVCRT.@)
533  */
534 double CDECL _CIsqrt(void)
535 {
536   FPU_DOUBLE(x);
537   return MSVCRT_sqrt(x);
538 }
539
540 /*********************************************************************
541  *              _CItan (MSVCRT.@)
542  */
543 double CDECL _CItan(void)
544 {
545   FPU_DOUBLE(x);
546   return MSVCRT_tan(x);
547 }
548
549 /*********************************************************************
550  *              _CItanh (MSVCRT.@)
551  */
552 double CDECL _CItanh(void)
553 {
554   FPU_DOUBLE(x);
555   return MSVCRT_tanh(x);
556 }
557
558 #endif /* defined(__GNUC__) && defined(__i386__) */
559
560 /*********************************************************************
561  *              _fpclass (MSVCRT.@)
562  */
563 int CDECL _fpclass(double num)
564 {
565 #if defined(HAVE_FPCLASS) || defined(fpclass)
566   switch (fpclass( num ))
567   {
568 #ifdef FP_SNAN
569   case FP_SNAN:  return MSVCRT__FPCLASS_SNAN;
570 #endif
571 #ifdef FP_QNAN
572   case FP_QNAN:  return MSVCRT__FPCLASS_QNAN;
573 #endif
574 #ifdef FP_NINF
575   case FP_NINF:  return MSVCRT__FPCLASS_NINF;
576 #endif
577 #ifdef FP_PINF
578   case FP_PINF:  return MSVCRT__FPCLASS_PINF;
579 #endif
580 #ifdef FP_NDENORM
581   case FP_NDENORM: return MSVCRT__FPCLASS_ND;
582 #endif
583 #ifdef FP_PDENORM
584   case FP_PDENORM: return MSVCRT__FPCLASS_PD;
585 #endif
586 #ifdef FP_NZERO
587   case FP_NZERO: return MSVCRT__FPCLASS_NZ;
588 #endif
589 #ifdef FP_PZERO
590   case FP_PZERO: return MSVCRT__FPCLASS_PZ;
591 #endif
592 #ifdef FP_NNORM
593   case FP_NNORM: return MSVCRT__FPCLASS_NN;
594 #endif
595 #ifdef FP_PNORM
596   case FP_PNORM: return MSVCRT__FPCLASS_PN;
597 #endif
598   default: return MSVCRT__FPCLASS_PN;
599   }
600 #elif defined (fpclassify)
601   switch (fpclassify( num ))
602   {
603   case FP_NAN: return MSVCRT__FPCLASS_QNAN;
604   case FP_INFINITE: return signbit(num) ? MSVCRT__FPCLASS_NINF : MSVCRT__FPCLASS_PINF;
605   case FP_SUBNORMAL: return signbit(num) ?MSVCRT__FPCLASS_ND : MSVCRT__FPCLASS_PD;
606   case FP_ZERO: return signbit(num) ? MSVCRT__FPCLASS_NZ : MSVCRT__FPCLASS_PZ;
607   }
608   return signbit(num) ? MSVCRT__FPCLASS_NN : MSVCRT__FPCLASS_PN;
609 #else
610   if (!finite(num))
611     return MSVCRT__FPCLASS_QNAN;
612   return num == 0.0 ? MSVCRT__FPCLASS_PZ : (num < 0 ? MSVCRT__FPCLASS_NN : MSVCRT__FPCLASS_PN);
613 #endif
614 }
615
616 /*********************************************************************
617  *              _rotl (MSVCRT.@)
618  */
619 unsigned int CDECL _rotl(unsigned int num, int shift)
620 {
621   shift &= 31;
622   return (num << shift) | (num >> (32-shift));
623 }
624
625 /*********************************************************************
626  *              _logb (MSVCRT.@)
627  */
628 double CDECL _logb(double num)
629 {
630   if (!finite(num)) *MSVCRT__errno() = MSVCRT_EDOM;
631   return logb(num);
632 }
633
634 /*********************************************************************
635  *              _lrotl (MSVCRT.@)
636  */
637 MSVCRT_ulong CDECL MSVCRT__lrotl(MSVCRT_ulong num, int shift)
638 {
639   shift &= 0x1f;
640   return (num << shift) | (num >> (32-shift));
641 }
642
643 /*********************************************************************
644  *              _lrotr (MSVCRT.@)
645  */
646 MSVCRT_ulong CDECL MSVCRT__lrotr(MSVCRT_ulong num, int shift)
647 {
648   shift &= 0x1f;
649   return (num >> shift) | (num << (32-shift));
650 }
651
652 /*********************************************************************
653  *              _rotr (MSVCRT.@)
654  */
655 unsigned int CDECL _rotr(unsigned int num, int shift)
656 {
657     shift &= 0x1f;
658     return (num >> shift) | (num << (32-shift));
659 }
660
661 /*********************************************************************
662  *              _scalb (MSVCRT.@)
663  */
664 double CDECL MSVCRT__scalb(double num, MSVCRT_long power)
665 {
666   if (!finite(num)) *MSVCRT__errno() = MSVCRT_EDOM;
667   return ldexp(num, power);
668 }
669
670 /*********************************************************************
671  *              _hypot (MSVCRT.@)
672  */
673 double CDECL _hypot(double x, double y)
674 {
675   /* FIXME: errno handling */
676   return hypot( x, y );
677 }
678
679 /*********************************************************************
680  *      _hypotf (MSVCRT.@)
681  */
682 float CDECL _hypotf(float x, float y)
683 {
684   /* FIXME: errno handling */
685   return hypotf( x, y );
686 }
687
688 /*********************************************************************
689  *              ceil (MSVCRT.@)
690  */
691 double CDECL MSVCRT_ceil( double x )
692 {
693   return ceil(x);
694 }
695
696 /*********************************************************************
697  *              floor (MSVCRT.@)
698  */
699 double CDECL MSVCRT_floor( double x )
700 {
701   return floor(x);
702 }
703
704 /*********************************************************************
705  *              fabs (MSVCRT.@)
706  */
707 double CDECL MSVCRT_fabs( double x )
708 {
709   return fabs(x);
710 }
711
712 /*********************************************************************
713  *              frexp (MSVCRT.@)
714  */
715 double CDECL MSVCRT_frexp( double x, int *exp )
716 {
717   return frexp( x, exp );
718 }
719
720 /*********************************************************************
721  *              modf (MSVCRT.@)
722  */
723 double CDECL MSVCRT_modf( double x, double *iptr )
724 {
725   return modf( x, iptr );
726 }
727
728 /*********************************************************************
729  *              _matherr (MSVCRT.@)
730  */
731 int CDECL MSVCRT__matherr(struct MSVCRT__exception *e)
732 {
733   if (e)
734     TRACE("(%p = %d, %s, %g %g %g)\n",e, e->type, e->name, e->arg1, e->arg2,
735           e->retval);
736   else
737     TRACE("(null)\n");
738   if (MSVCRT_default_matherr_func)
739     return MSVCRT_default_matherr_func(e);
740   ERR(":Unhandled math error!\n");
741   return 0;
742 }
743
744 /*********************************************************************
745  *              __setusermatherr (MSVCRT.@)
746  */
747 void CDECL MSVCRT___setusermatherr(MSVCRT_matherr_func func)
748 {
749   MSVCRT_default_matherr_func = func;
750   TRACE(":new matherr handler %p\n", func);
751 }
752
753 /**********************************************************************
754  *              _statusfp (MSVCRT.@)
755  */
756 unsigned int CDECL _statusfp(void)
757 {
758    unsigned int retVal = 0;
759 #if defined(__GNUC__) && defined(__i386__)
760   unsigned int fpword;
761
762   __asm__ __volatile__( "fstsw %0" : "=m" (fpword) : );
763   if (fpword & 0x1)  retVal |= MSVCRT__SW_INVALID;
764   if (fpword & 0x2)  retVal |= MSVCRT__SW_DENORMAL;
765   if (fpword & 0x4)  retVal |= MSVCRT__SW_ZERODIVIDE;
766   if (fpword & 0x8)  retVal |= MSVCRT__SW_OVERFLOW;
767   if (fpword & 0x10) retVal |= MSVCRT__SW_UNDERFLOW;
768   if (fpword & 0x20) retVal |= MSVCRT__SW_INEXACT;
769 #else
770   FIXME(":Not implemented!\n");
771 #endif
772   return retVal;
773 }
774
775 /*********************************************************************
776  *              _clearfp (MSVCRT.@)
777  */
778 unsigned int CDECL _clearfp(void)
779 {
780   unsigned int retVal = _statusfp();
781 #if defined(__GNUC__) && defined(__i386__)
782   __asm__ __volatile__( "fnclex" );
783 #else
784   FIXME(":Not Implemented\n");
785 #endif
786   return retVal;
787 }
788
789 /*********************************************************************
790  *              __fpecode (MSVCRT.@)
791  */
792 int * CDECL __fpecode(void)
793 {
794     return &msvcrt_get_thread_data()->fpecode;
795 }
796
797 /*********************************************************************
798  *              ldexp (MSVCRT.@)
799  */
800 double CDECL MSVCRT_ldexp(double num, MSVCRT_long exp)
801 {
802   double z = ldexp(num,exp);
803
804   if (!finite(z))
805     *MSVCRT__errno() = MSVCRT_ERANGE;
806   else if (z == 0 && signbit(z))
807     z = 0.0; /* Convert -0 -> +0 */
808   return z;
809 }
810
811 /*********************************************************************
812  *              _cabs (MSVCRT.@)
813  */
814 double CDECL MSVCRT__cabs(struct MSVCRT__complex num)
815 {
816   return sqrt(num.x * num.x + num.y * num.y);
817 }
818
819 /*********************************************************************
820  *              _chgsign (MSVCRT.@)
821  */
822 double CDECL _chgsign(double num)
823 {
824   /* FIXME: +-infinity,Nan not tested */
825   return -num;
826 }
827
828 /*********************************************************************
829  *              _control87 (MSVCRT.@)
830  */
831 unsigned int CDECL _control87(unsigned int newval, unsigned int mask)
832 {
833 #if defined(__GNUC__) && defined(__i386__)
834   unsigned int fpword = 0;
835   unsigned int flags = 0;
836
837   TRACE("(%08x, %08x): Called\n", newval, mask);
838
839   /* Get fp control word */
840   __asm__ __volatile__( "fstcw %0" : "=m" (fpword) : );
841
842   TRACE("Control word before : %08x\n", fpword);
843
844   /* Convert into mask constants */
845   if (fpword & 0x1)  flags |= MSVCRT__EM_INVALID;
846   if (fpword & 0x2)  flags |= MSVCRT__EM_DENORMAL;
847   if (fpword & 0x4)  flags |= MSVCRT__EM_ZERODIVIDE;
848   if (fpword & 0x8)  flags |= MSVCRT__EM_OVERFLOW;
849   if (fpword & 0x10) flags |= MSVCRT__EM_UNDERFLOW;
850   if (fpword & 0x20) flags |= MSVCRT__EM_INEXACT;
851   switch(fpword & 0xC00) {
852   case 0xC00: flags |= MSVCRT__RC_UP|MSVCRT__RC_DOWN; break;
853   case 0x800: flags |= MSVCRT__RC_UP; break;
854   case 0x400: flags |= MSVCRT__RC_DOWN; break;
855   }
856   switch(fpword & 0x300) {
857   case 0x0:   flags |= MSVCRT__PC_24; break;
858   case 0x200: flags |= MSVCRT__PC_53; break;
859   case 0x300: flags |= MSVCRT__PC_64; break;
860   }
861   if (fpword & 0x1000) flags |= MSVCRT__IC_AFFINE;
862
863   /* Mask with parameters */
864   flags = (flags & ~mask) | (newval & mask);
865
866   /* Convert (masked) value back to fp word */
867   fpword = 0;
868   if (flags & MSVCRT__EM_INVALID)    fpword |= 0x1;
869   if (flags & MSVCRT__EM_DENORMAL)   fpword |= 0x2;
870   if (flags & MSVCRT__EM_ZERODIVIDE) fpword |= 0x4;
871   if (flags & MSVCRT__EM_OVERFLOW)   fpword |= 0x8;
872   if (flags & MSVCRT__EM_UNDERFLOW)  fpword |= 0x10;
873   if (flags & MSVCRT__EM_INEXACT)    fpword |= 0x20;
874   switch(flags & (MSVCRT__RC_UP | MSVCRT__RC_DOWN)) {
875   case MSVCRT__RC_UP|MSVCRT__RC_DOWN: fpword |= 0xC00; break;
876   case MSVCRT__RC_UP:          fpword |= 0x800; break;
877   case MSVCRT__RC_DOWN:        fpword |= 0x400; break;
878   }
879   switch (flags & (MSVCRT__PC_24 | MSVCRT__PC_53)) {
880   case MSVCRT__PC_64: fpword |= 0x300; break;
881   case MSVCRT__PC_53: fpword |= 0x200; break;
882   case MSVCRT__PC_24: fpword |= 0x0; break;
883   }
884   if (flags & MSVCRT__IC_AFFINE) fpword |= 0x1000;
885
886   TRACE("Control word after  : %08x\n", fpword);
887
888   /* Put fp control word */
889   __asm__ __volatile__( "fldcw %0" : : "m" (fpword) );
890
891   return flags;
892 #else
893   FIXME(":Not Implemented!\n");
894   return 0;
895 #endif
896 }
897
898 /*********************************************************************
899  *              _controlfp (MSVCRT.@)
900  */
901 unsigned int CDECL _controlfp(unsigned int newval, unsigned int mask)
902 {
903 #ifdef __i386__
904   return _control87( newval, mask & ~MSVCRT__EM_DENORMAL );
905 #else
906   FIXME(":Not Implemented!\n");
907   return 0;
908 #endif
909 }
910
911 /*********************************************************************
912  *              _controlfp_s (MSVCRT.@)
913  */
914 int CDECL _controlfp_s(unsigned int *cur, unsigned int newval, unsigned int mask)
915 {
916 #ifdef __i386__
917     unsigned int flags;
918
919     FIXME("(%p %u %u) semi-stub\n", cur, newval, mask);
920
921     flags = _control87( newval, mask & ~MSVCRT__EM_DENORMAL );
922
923     if(cur)
924         *cur = flags;
925
926     return 0;
927 #else
928     FIXME(":Not Implemented!\n");
929     return 0;
930 #endif
931 }
932
933 /*********************************************************************
934  *              _copysign (MSVCRT.@)
935  */
936 double CDECL _copysign(double num, double sign)
937 {
938   /* FIXME: Behaviour for Nan/Inf? */
939   if (sign < 0.0)
940     return num < 0.0 ? num : -num;
941   return num < 0.0 ? -num : num;
942 }
943
944 /*********************************************************************
945  *              _finite (MSVCRT.@)
946  */
947 int CDECL _finite(double num)
948 {
949   return (finite(num)?1:0); /* See comment for _isnan() */
950 }
951
952 /*********************************************************************
953  *              _fpreset (MSVCRT.@)
954  */
955 void CDECL _fpreset(void)
956 {
957 #if defined(__GNUC__) && defined(__i386__)
958   __asm__ __volatile__( "fninit" );
959 #else
960   FIXME(":Not Implemented!\n");
961 #endif
962 }
963
964 /*********************************************************************
965  *              _isnan (MSVCRT.@)
966  */
967 INT CDECL _isnan(double num)
968 {
969   /* Some implementations return -1 for true(glibc), msvcrt/crtdll return 1.
970    * Do the same, as the result may be used in calculations
971    */
972   return isnan(num) ? 1 : 0;
973 }
974
975 /*********************************************************************
976  *              _j0 (MSVCRT.@)
977  */
978 double CDECL _j0(double num)
979 {
980   /* FIXME: errno handling */
981   return j0(num);
982 }
983
984 /*********************************************************************
985  *              _j1 (MSVCRT.@)
986  */
987 double CDECL _j1(double num)
988 {
989   /* FIXME: errno handling */
990   return j1(num);
991 }
992
993 /*********************************************************************
994  *              jn (MSVCRT.@)
995  */
996 double CDECL _jn(int n, double num)
997 {
998   /* FIXME: errno handling */
999   return jn(n, num);
1000 }
1001
1002 /*********************************************************************
1003  *              _y0 (MSVCRT.@)
1004  */
1005 double CDECL _y0(double num)
1006 {
1007   double retval;
1008   if (!finite(num)) *MSVCRT__errno() = MSVCRT_EDOM;
1009   retval  = y0(num);
1010   if (_fpclass(retval) == MSVCRT__FPCLASS_NINF)
1011   {
1012     *MSVCRT__errno() = MSVCRT_EDOM;
1013     retval = sqrt(-1);
1014   }
1015   return retval;
1016 }
1017
1018 /*********************************************************************
1019  *              _y1 (MSVCRT.@)
1020  */
1021 double CDECL _y1(double num)
1022 {
1023   double retval;
1024   if (!finite(num)) *MSVCRT__errno() = MSVCRT_EDOM;
1025   retval  = y1(num);
1026   if (_fpclass(retval) == MSVCRT__FPCLASS_NINF)
1027   {
1028     *MSVCRT__errno() = MSVCRT_EDOM;
1029     retval = sqrt(-1);
1030   }
1031   return retval;
1032 }
1033
1034 /*********************************************************************
1035  *              _yn (MSVCRT.@)
1036  */
1037 double CDECL _yn(int order, double num)
1038 {
1039   double retval;
1040   if (!finite(num)) *MSVCRT__errno() = MSVCRT_EDOM;
1041   retval  = yn(order,num);
1042   if (_fpclass(retval) == MSVCRT__FPCLASS_NINF)
1043   {
1044     *MSVCRT__errno() = MSVCRT_EDOM;
1045     retval = sqrt(-1);
1046   }
1047   return retval;
1048 }
1049
1050 /*********************************************************************
1051  *              _nextafter (MSVCRT.@)
1052  */
1053 double CDECL _nextafter(double num, double next)
1054 {
1055   double retval;
1056   if (!finite(num) || !finite(next)) *MSVCRT__errno() = MSVCRT_EDOM;
1057   retval = nextafter(num,next);
1058   return retval;
1059 }
1060
1061 /*********************************************************************
1062  *              _ecvt (MSVCRT.@)
1063  */
1064 char * CDECL _ecvt( double number, int ndigits, int *decpt, int *sign )
1065 {
1066     int prec, len;
1067     thread_data_t *data = msvcrt_get_thread_data();
1068     /* FIXME: check better for overflow (native supports over 300 chars's) */
1069     ndigits = min( ndigits, 80 - 7); /* 7 : space for dec point, 1 for "e",
1070                                       * 4 for exponent and one for
1071                                       * terminating '\0' */
1072     if (!data->efcvt_buffer)
1073         data->efcvt_buffer = MSVCRT_malloc( 80 ); /* ought to be enough */
1074
1075     if( number < 0) {
1076         *sign = TRUE;
1077         number = -number;
1078     } else
1079         *sign = FALSE;
1080     /* handle cases with zero ndigits or less */
1081     prec = ndigits;
1082     if( prec < 1) prec = 2;
1083     len = snprintf(data->efcvt_buffer, 80, "%.*le", prec - 1, number);
1084     /* take the decimal "point away */
1085     if( prec != 1)
1086         memmove( data->efcvt_buffer + 1, data->efcvt_buffer + 2, len - 1 );
1087     /* take the exponential "e" out */
1088     data->efcvt_buffer[ prec] = '\0';
1089     /* read the exponent */
1090     sscanf( data->efcvt_buffer + prec + 1, "%d", decpt);
1091     (*decpt)++;
1092     /* adjust for some border cases */
1093     if( data->efcvt_buffer[0] == '0')/* value is zero */
1094         *decpt = 0;
1095     /* handle cases with zero ndigits or less */
1096     if( ndigits < 1){
1097         if( data->efcvt_buffer[ 0] >= '5')
1098             (*decpt)++;
1099         data->efcvt_buffer[ 0] = '\0';
1100     }
1101     TRACE("out=\"%s\"\n",data->efcvt_buffer);
1102     return data->efcvt_buffer;
1103 }
1104
1105 /***********************************************************************
1106  *              _fcvt  (MSVCRT.@)
1107  */
1108 char * CDECL _fcvt( double number, int ndigits, int *decpt, int *sign )
1109 {
1110     thread_data_t *data = msvcrt_get_thread_data();
1111     int stop, dec1, dec2;
1112     char *ptr1, *ptr2, *first;
1113     char buf[80]; /* ought to be enough */
1114
1115     if (!data->efcvt_buffer)
1116         data->efcvt_buffer = MSVCRT_malloc( 80 ); /* ought to be enough */
1117
1118     if (number < 0)
1119     {
1120         *sign = 1;
1121         number = -number;
1122     } else *sign = 0;
1123
1124     snprintf(buf, 80, "%.*f", ndigits < 0 ? 0 : ndigits, number);
1125     ptr1 = buf;
1126     ptr2 = data->efcvt_buffer;
1127     first = NULL;
1128     dec1 = 0;
1129     dec2 = 0;
1130
1131     /* For numbers below the requested resolution, work out where
1132        the decimal point will be rather than finding it in the string */
1133     if (number < 1.0 && number > 0.0) {
1134         dec2 = log10(number + 1e-10);
1135         if (-dec2 <= ndigits) dec2 = 0;
1136     }
1137
1138     /* If requested digits is zero or less, we will need to truncate
1139      * the returned string */
1140     if (ndigits < 1) {
1141         stop = strlen(buf) + ndigits;
1142     } else {
1143         stop = strlen(buf);
1144     }
1145
1146     while (*ptr1 == '0') ptr1++; /* Skip leading zeroes */
1147     while (*ptr1 != '\0' && *ptr1 != '.') {
1148         if (!first) first = ptr2;
1149         if ((ptr1 - buf) < stop) {
1150             *ptr2++ = *ptr1++;
1151         } else {
1152             ptr1++;
1153         }
1154         dec1++;
1155     }
1156
1157     if (ndigits > 0) {
1158         ptr1++;
1159         if (!first) {
1160             while (*ptr1 == '0') { /* Process leading zeroes */
1161                 *ptr2++ = *ptr1++;
1162                 dec1--;
1163             }
1164         }
1165         while (*ptr1 != '\0') {
1166             if (!first) first = ptr2;
1167             *ptr2++ = *ptr1++;
1168         }
1169     }
1170
1171     *ptr2 = '\0';
1172
1173     /* We never found a non-zero digit, then our number is either
1174      * smaller than the requested precision, or 0.0 */
1175     if (!first) {
1176         if (number > 0.0) {
1177             first = ptr2;
1178         } else {
1179             first = data->efcvt_buffer;
1180             dec1 = 0;
1181         }
1182     }
1183
1184     *decpt = dec2 ? dec2 : dec1;
1185     return first;
1186 }
1187
1188 /***********************************************************************
1189  *              _gcvt  (MSVCRT.@)
1190  *
1191  * FIXME: uses both E and F.
1192  */
1193 char * CDECL _gcvt( double number, int ndigit, char *buff )
1194 {
1195     sprintf(buff, "%.*E", ndigit, number);
1196     return buff;
1197 }
1198
1199 #include <stdlib.h> /* div_t, ldiv_t */
1200
1201 /*********************************************************************
1202  *              div (MSVCRT.@)
1203  * VERSION
1204  *      [i386] Windows binary compatible - returns the struct in eax/edx.
1205  */
1206 #ifdef __i386__
1207 unsigned __int64 CDECL MSVCRT_div(int num, int denom)
1208 {
1209   div_t dt = div(num,denom);
1210   return ((unsigned __int64)dt.rem << 32) | (unsigned int)dt.quot;
1211 }
1212 #else
1213 /*********************************************************************
1214  *              div (MSVCRT.@)
1215  * VERSION
1216  *      [!i386] Non-x86 can't run win32 apps so we don't need binary compatibility
1217  */
1218 MSVCRT_div_t CDECL MSVCRT_div(int num, int denom)
1219 {
1220   div_t dt = div(num,denom);
1221   MSVCRT_div_t     ret;
1222   ret.quot = dt.quot;
1223   ret.rem = dt.rem;
1224
1225   return ret;
1226
1227 }
1228 #endif /* ifdef __i386__ */
1229
1230
1231 /*********************************************************************
1232  *              ldiv (MSVCRT.@)
1233  * VERSION
1234  *      [i386] Windows binary compatible - returns the struct in eax/edx.
1235  */
1236 #ifdef __i386__
1237 unsigned __int64 CDECL MSVCRT_ldiv(MSVCRT_long num, MSVCRT_long denom)
1238 {
1239   ldiv_t ldt = ldiv(num,denom);
1240   return ((unsigned __int64)ldt.rem << 32) | (MSVCRT_ulong)ldt.quot;
1241 }
1242 #else
1243 /*********************************************************************
1244  *              ldiv (MSVCRT.@)
1245  * VERSION
1246  *      [!i386] Non-x86 can't run win32 apps so we don't need binary compatibility
1247  */
1248 MSVCRT_ldiv_t CDECL MSVCRT_ldiv(MSVCRT_long num, MSVCRT_long denom)
1249 {
1250   ldiv_t result = ldiv(num,denom);
1251
1252   MSVCRT_ldiv_t ret;
1253   ret.quot = result.quot;
1254   ret.rem = result.rem;
1255
1256   return ret;
1257 }
1258 #endif /* ifdef __i386__ */
1259
1260 #ifdef __i386__
1261
1262 /*********************************************************************
1263  *              _adjust_fdiv (MSVCRT.@)
1264  * Used by the MSVC compiler to work around the Pentium FDIV bug.
1265  */
1266 int MSVCRT__adjust_fdiv = 0;
1267
1268 /***********************************************************************
1269  *              _adj_fdiv_m16i (MSVCRT.@)
1270  *
1271  * NOTE
1272  *    I _think_ this function is intended to work around the Pentium
1273  *    fdiv bug.
1274  */
1275 void __stdcall _adj_fdiv_m16i( short arg )
1276 {
1277   TRACE("(): stub\n");
1278 }
1279
1280 /***********************************************************************
1281  *              _adj_fdiv_m32 (MSVCRT.@)
1282  *
1283  * NOTE
1284  *    I _think_ this function is intended to work around the Pentium
1285  *    fdiv bug.
1286  */
1287 void __stdcall _adj_fdiv_m32( unsigned int arg )
1288 {
1289   TRACE("(): stub\n");
1290 }
1291
1292 /***********************************************************************
1293  *              _adj_fdiv_m32i (MSVCRT.@)
1294  *
1295  * NOTE
1296  *    I _think_ this function is intended to work around the Pentium
1297  *    fdiv bug.
1298  */
1299 void __stdcall _adj_fdiv_m32i( int arg )
1300 {
1301   TRACE("(): stub\n");
1302 }
1303
1304 /***********************************************************************
1305  *              _adj_fdiv_m64 (MSVCRT.@)
1306  *
1307  * NOTE
1308  *    I _think_ this function is intended to work around the Pentium
1309  *    fdiv bug.
1310  */
1311 void __stdcall _adj_fdiv_m64( unsigned __int64 arg )
1312 {
1313   TRACE("(): stub\n");
1314 }
1315
1316 /***********************************************************************
1317  *              _adj_fdiv_r (MSVCRT.@)
1318  * FIXME
1319  *    This function is likely to have the wrong number of arguments.
1320  *
1321  * NOTE
1322  *    I _think_ this function is intended to work around the Pentium
1323  *    fdiv bug.
1324  */
1325 void _adj_fdiv_r(void)
1326 {
1327   TRACE("(): stub\n");
1328 }
1329
1330 /***********************************************************************
1331  *              _adj_fdivr_m16i (MSVCRT.@)
1332  *
1333  * NOTE
1334  *    I _think_ this function is intended to work around the Pentium
1335  *    fdiv bug.
1336  */
1337 void __stdcall _adj_fdivr_m16i( short arg )
1338 {
1339   TRACE("(): stub\n");
1340 }
1341
1342 /***********************************************************************
1343  *              _adj_fdivr_m32 (MSVCRT.@)
1344  *
1345  * NOTE
1346  *    I _think_ this function is intended to work around the Pentium
1347  *    fdiv bug.
1348  */
1349 void __stdcall _adj_fdivr_m32( unsigned int arg )
1350 {
1351   TRACE("(): stub\n");
1352 }
1353
1354 /***********************************************************************
1355  *              _adj_fdivr_m32i (MSVCRT.@)
1356  *
1357  * NOTE
1358  *    I _think_ this function is intended to work around the Pentium
1359  *    fdiv bug.
1360  */
1361 void __stdcall _adj_fdivr_m32i( int arg )
1362 {
1363   TRACE("(): stub\n");
1364 }
1365
1366 /***********************************************************************
1367  *              _adj_fdivr_m64 (MSVCRT.@)
1368  *
1369  * NOTE
1370  *    I _think_ this function is intended to work around the Pentium
1371  *    fdiv bug.
1372  */
1373 void __stdcall _adj_fdivr_m64( unsigned __int64 arg )
1374 {
1375   TRACE("(): stub\n");
1376 }
1377
1378 /***********************************************************************
1379  *              _adj_fpatan (MSVCRT.@)
1380  * FIXME
1381  *    This function is likely to have the wrong number of arguments.
1382  *
1383  * NOTE
1384  *    I _think_ this function is intended to work around the Pentium
1385  *    fdiv bug.
1386  */
1387 void _adj_fpatan(void)
1388 {
1389   TRACE("(): stub\n");
1390 }
1391
1392 /***********************************************************************
1393  *              _adj_fprem (MSVCRT.@)
1394  * FIXME
1395  *    This function is likely to have the wrong number of arguments.
1396  *
1397  * NOTE
1398  *    I _think_ this function is intended to work around the Pentium
1399  *    fdiv bug.
1400  */
1401 void _adj_fprem(void)
1402 {
1403   TRACE("(): stub\n");
1404 }
1405
1406 /***********************************************************************
1407  *              _adj_fprem1 (MSVCRT.@)
1408  * FIXME
1409  *    This function is likely to have the wrong number of arguments.
1410  *
1411  * NOTE
1412  *    I _think_ this function is intended to work around the Pentium
1413  *    fdiv bug.
1414  */
1415 void _adj_fprem1(void)
1416 {
1417   TRACE("(): stub\n");
1418 }
1419
1420 /***********************************************************************
1421  *              _adj_fptan (MSVCRT.@)
1422  * FIXME
1423  *    This function is likely to have the wrong number of arguments.
1424  *
1425  * NOTE
1426  *    I _think_ this function is intended to work around the Pentium
1427  *    fdiv bug.
1428  */
1429 void _adj_fptan(void)
1430 {
1431   TRACE("(): stub\n");
1432 }
1433
1434 /***********************************************************************
1435  *              _safe_fdiv (MSVCRT.@)
1436  * FIXME
1437  *    This function is likely to have the wrong number of arguments.
1438  *
1439  * NOTE
1440  *    I _think_ this function is intended to work around the Pentium
1441  *    fdiv bug.
1442  */
1443 void _safe_fdiv(void)
1444 {
1445   TRACE("(): stub\n");
1446 }
1447
1448 /***********************************************************************
1449  *              _safe_fdivr (MSVCRT.@)
1450  * FIXME
1451  *    This function is likely to have the wrong number of arguments.
1452  *
1453  * NOTE
1454  *    I _think_ this function is intended to work around the Pentium
1455  *    fdiv bug.
1456  */
1457 void _safe_fdivr(void)
1458 {
1459   TRACE("(): stub\n");
1460 }
1461
1462 /***********************************************************************
1463  *              _safe_fprem (MSVCRT.@)
1464  * FIXME
1465  *    This function is likely to have the wrong number of arguments.
1466  *
1467  * NOTE
1468  *    I _think_ this function is intended to work around the Pentium
1469  *    fdiv bug.
1470  */
1471 void _safe_fprem(void)
1472 {
1473   TRACE("(): stub\n");
1474 }
1475
1476 /***********************************************************************
1477  *              _safe_fprem1 (MSVCRT.@)
1478  *
1479  * FIXME
1480  *    This function is likely to have the wrong number of arguments.
1481  *
1482  * NOTE
1483  *    I _think_ this function is intended to work around the Pentium
1484  *    fdiv bug.
1485  */
1486 void _safe_fprem1(void)
1487 {
1488   TRACE("(): stub\n");
1489 }
1490
1491 #endif  /* __i386__ */