xref: /netbsd-src/lib/libc/compat/gen/compat_modf_ieee754.c (revision ace5b9b5feb0e7608bd2da7a617428d2e1cf8aa3)
1*ace5b9b5Schristos /* $NetBSD: compat_modf_ieee754.c,v 1.6 2024/01/20 14:52:45 christos Exp $ */
200483774Sdrochner 
300483774Sdrochner /*
400483774Sdrochner  * Copyright (c) 1994, 1995 Carnegie-Mellon University.
500483774Sdrochner  * All rights reserved.
600483774Sdrochner  *
700483774Sdrochner  * Author: Chris G. Demetriou
800483774Sdrochner  *
900483774Sdrochner  * Permission to use, copy, modify and distribute this software and
1000483774Sdrochner  * its documentation is hereby granted, provided that both the copyright
1100483774Sdrochner  * notice and this permission notice appear in all copies of the
1200483774Sdrochner  * software, derivative works or modified versions, and any portions
1300483774Sdrochner  * thereof, and that both notices appear in supporting documentation.
1400483774Sdrochner  *
1500483774Sdrochner  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
1600483774Sdrochner  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
1700483774Sdrochner  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
1800483774Sdrochner  *
1900483774Sdrochner  * Carnegie Mellon requests users of this software to return to
2000483774Sdrochner  *
2100483774Sdrochner  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
2200483774Sdrochner  *  School of Computer Science
2300483774Sdrochner  *  Carnegie Mellon University
2400483774Sdrochner  *  Pittsburgh PA 15213-3890
2500483774Sdrochner  *
2600483774Sdrochner  * any improvements or extensions that they make and grant Carnegie the
2700483774Sdrochner  * rights to redistribute these changes.
2800483774Sdrochner  */
2900483774Sdrochner 
3000483774Sdrochner #include <sys/types.h>
3100483774Sdrochner #include <machine/ieee.h>
3200483774Sdrochner #include <errno.h>
33*ace5b9b5Schristos #include <math.h>
3400483774Sdrochner 
3500483774Sdrochner /*
3600483774Sdrochner  * double modf(double val, double *iptr)
3700483774Sdrochner  * returns: f and i such that |f| < 1.0, (f + i) = val, and
3800483774Sdrochner  *	sign(f) == sign(i) == sign(val).
3900483774Sdrochner  *
4000483774Sdrochner  * Beware signedness when doing subtraction, and also operand size!
4100483774Sdrochner  */
4200483774Sdrochner double
modf(double val,double * iptr)4300483774Sdrochner modf(double val, double *iptr)
4400483774Sdrochner {
4500483774Sdrochner 	union ieee_double_u u, v;
4600483774Sdrochner 	u_int64_t frac;
4700483774Sdrochner 
4800483774Sdrochner 	/*
496cbc5cbfSdrochner 	 * If input is +/-Inf or NaN, return +/-0 or NaN.
5000483774Sdrochner 	 */
5100483774Sdrochner 	u.dblu_d = val;
526cbc5cbfSdrochner 	if (u.dblu_dbl.dbl_exp == DBL_EXP_INFNAN) {
536cbc5cbfSdrochner 		*iptr = u.dblu_d;
546cbc5cbfSdrochner 		return (0.0 / u.dblu_d);
556cbc5cbfSdrochner 	}
5600483774Sdrochner 
5700483774Sdrochner 	/*
5800483774Sdrochner 	 * If input can't have a fractional part, return
5900483774Sdrochner 	 * (appropriately signed) zero, and make i be the input.
6000483774Sdrochner 	 */
6100483774Sdrochner 	if ((int)u.dblu_dbl.dbl_exp - DBL_EXP_BIAS > DBL_FRACBITS - 1) {
6200483774Sdrochner 		*iptr = u.dblu_d;
6300483774Sdrochner 		v.dblu_d = 0.0;
6400483774Sdrochner 		v.dblu_dbl.dbl_sign = u.dblu_dbl.dbl_sign;
6500483774Sdrochner 		return (v.dblu_d);
6600483774Sdrochner 	}
6700483774Sdrochner 
6800483774Sdrochner 	/*
6900483774Sdrochner 	 * If |input| < 1.0, return it, and set i to the appropriately
7000483774Sdrochner 	 * signed zero.
7100483774Sdrochner 	 */
7200483774Sdrochner 	if (u.dblu_dbl.dbl_exp < DBL_EXP_BIAS) {
7300483774Sdrochner 		v.dblu_d = 0.0;
7400483774Sdrochner 		v.dblu_dbl.dbl_sign = u.dblu_dbl.dbl_sign;
7500483774Sdrochner 		*iptr = v.dblu_d;
7600483774Sdrochner 		return (u.dblu_d);
7700483774Sdrochner 	}
7800483774Sdrochner 
7900483774Sdrochner 	/*
8000483774Sdrochner 	 * There can be a fractional part of the input.
8100483774Sdrochner 	 * If you look at the math involved for a few seconds, it's
8200483774Sdrochner 	 * plain to see that the integral part is the input, with the
8300483774Sdrochner 	 * low (DBL_FRACBITS - (exponent - DBL_EXP_BIAS)) bits zeroed,
8400483774Sdrochner 	 * the fractional part is the part with the rest of the
8500483774Sdrochner 	 * bits zeroed.  Just zeroing the high bits to get the
8600483774Sdrochner 	 * fractional part would yield a fraction in need of
8700483774Sdrochner 	 * normalization.  Therefore, we take the easy way out, and
8800483774Sdrochner 	 * just use subtraction to get the fractional part.
8900483774Sdrochner 	 */
9000483774Sdrochner 	v.dblu_d = u.dblu_d;
9100483774Sdrochner 	/* Zero the low bits of the fraction, the sleazy way. */
9200483774Sdrochner 	frac = ((u_int64_t)v.dblu_dbl.dbl_frach << 32) + v.dblu_dbl.dbl_fracl;
9300483774Sdrochner 	frac >>= DBL_FRACBITS - (u.dblu_dbl.dbl_exp - DBL_EXP_BIAS);
9400483774Sdrochner 	frac <<= DBL_FRACBITS - (u.dblu_dbl.dbl_exp - DBL_EXP_BIAS);
951acb507cSchristos 	v.dblu_dbl.dbl_fracl = (unsigned int)(frac & 0xffffffffULL);
9600483774Sdrochner 	v.dblu_dbl.dbl_frach = (unsigned int)(frac >> 32);
9700483774Sdrochner 	*iptr = v.dblu_d;
9800483774Sdrochner 
9900483774Sdrochner 	u.dblu_d -= v.dblu_d;
10000483774Sdrochner 	u.dblu_dbl.dbl_sign = v.dblu_dbl.dbl_sign;
10100483774Sdrochner 	return (u.dblu_d);
10200483774Sdrochner }
103