blob: 301ce3b537b6c0eee5dbbc358587b66a3a341d2a [file] [log] [blame]
/*
* QEMU float support
*
* The code in this source file is derived from release 2a of the SoftFloat
* IEC/IEEE Floating-point Arithmetic Package. Those parts of the code (and
* some later contributions) are provided under that license, as detailed below.
* It has subsequently been modified by contributors to the QEMU Project,
* so some portions are provided under:
* the SoftFloat-2a license
* the BSD license
* GPL-v2-or-later
*
* Any future contributions to this file after December 1st 2014 will be
* taken to be licensed under the Softfloat-2a license unless specifically
* indicated otherwise.
*/
/*
===============================================================================
This C source file is part of the SoftFloat IEC/IEEE Floating-point
Arithmetic Package, Release 2a.
Written by John R. Hauser. This work was made possible in part by the
International Computer Science Institute, located at Suite 600, 1947 Center
Street, Berkeley, California 94704. Funding was partially provided by the
National Science Foundation under grant MIP-9311980. The original version
of this code was written as part of a project to build a fixed-point vector
processor in collaboration with the University of California at Berkeley,
overseen by Profs. Nelson Morgan and John Wawrzynek. More information
is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
arithmetic/SoftFloat.html'.
THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort
has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO
PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
Derivative works are acceptable, even for commercial purposes, so long as
(1) they include prominent notice that the work is derivative, and (2) they
include prominent notice akin to these four paragraphs for those parts of
this code that are retained.
===============================================================================
*/
/* BSD licensing:
* Copyright (c) 2006, Fabrice Bellard
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
/* Portions of this work are licensed under the terms of the GNU GPL,
* version 2 or later. See the COPYING file in the top-level directory.
*/
/* softfloat (and in particular the code in softfloat-specialize.h) is
* target-dependent and needs the TARGET_* macros.
*/
#include "qemu/osdep.h"
#include <math.h>
#include "qemu/bitops.h"
#include "fpu/softfloat.h"
/* We only need stdlib for abort() */
/*----------------------------------------------------------------------------
| Primitive arithmetic functions, including multi-word arithmetic, and
| division and square root approximations. (Can be specialized to target if
| desired.)
*----------------------------------------------------------------------------*/
#include "fpu/softfloat-macros.h"
/*
* Hardfloat
*
* Fast emulation of guest FP instructions is challenging for two reasons.
* First, FP instruction semantics are similar but not identical, particularly
* when handling NaNs. Second, emulating at reasonable speed the guest FP
* exception flags is not trivial: reading the host's flags register with a
* feclearexcept & fetestexcept pair is slow [slightly slower than soft-fp],
* and trapping on every FP exception is not fast nor pleasant to work with.
*
* We address these challenges by leveraging the host FPU for a subset of the
* operations. To do this we expand on the idea presented in this paper:
*
* Guo, Yu-Chuan, et al. "Translating the ARM Neon and VFP instructions in a
* binary translator." Software: Practice and Experience 46.12 (2016):1591-1615.
*
* The idea is thus to leverage the host FPU to (1) compute FP operations
* and (2) identify whether FP exceptions occurred while avoiding
* expensive exception flag register accesses.
*
* An important optimization shown in the paper is that given that exception
* flags are rarely cleared by the guest, we can avoid recomputing some flags.
* This is particularly useful for the inexact flag, which is very frequently
* raised in floating-point workloads.
*
* We optimize the code further by deferring to soft-fp whenever FP exception
* detection might get hairy. Two examples: (1) when at least one operand is
* denormal/inf/NaN; (2) when operands are not guaranteed to lead to a 0 result
* and the result is < the minimum normal.
*/
#define GEN_INPUT_FLUSH__NOCHECK(name, soft_t) \
static inline void name(soft_t *a, float_status *s) \
{ \
if (unlikely(soft_t ## _is_denormal(*a))) { \
*a = soft_t ## _set_sign(soft_t ## _zero, \
soft_t ## _is_neg(*a)); \
s->float_exception_flags |= float_flag_input_denormal; \
} \
}
GEN_INPUT_FLUSH__NOCHECK(float32_input_flush__nocheck, float32)
GEN_INPUT_FLUSH__NOCHECK(float64_input_flush__nocheck, float64)
#undef GEN_INPUT_FLUSH__NOCHECK
#define GEN_INPUT_FLUSH1(name, soft_t) \
static inline void name(soft_t *a, float_status *s) \
{ \
if (likely(!s->flush_inputs_to_zero)) { \
return; \
} \
soft_t ## _input_flush__nocheck(a, s); \
}
GEN_INPUT_FLUSH1(float32_input_flush1, float32)
GEN_INPUT_FLUSH1(float64_input_flush1, float64)
#undef GEN_INPUT_FLUSH1
#define GEN_INPUT_FLUSH2(name, soft_t) \
static inline void name(soft_t *a, soft_t *b, float_status *s) \
{ \
if (likely(!s->flush_inputs_to_zero)) { \
return; \
} \
soft_t ## _input_flush__nocheck(a, s); \
soft_t ## _input_flush__nocheck(b, s); \
}
GEN_INPUT_FLUSH2(float32_input_flush2, float32)
GEN_INPUT_FLUSH2(float64_input_flush2, float64)
#undef GEN_INPUT_FLUSH2
#define GEN_INPUT_FLUSH3(name, soft_t) \
static inline void name(soft_t *a, soft_t *b, soft_t *c, float_status *s) \
{ \
if (likely(!s->flush_inputs_to_zero)) { \
return; \
} \
soft_t ## _input_flush__nocheck(a, s); \
soft_t ## _input_flush__nocheck(b, s); \
soft_t ## _input_flush__nocheck(c, s); \
}
GEN_INPUT_FLUSH3(float32_input_flush3, float32)
GEN_INPUT_FLUSH3(float64_input_flush3, float64)
#undef GEN_INPUT_FLUSH3
/*
* Choose whether to use fpclassify or float32/64_* primitives in the generated
* hardfloat functions. Each combination of number of inputs and float size
* gets its own value.
*/
#if defined(__x86_64__)
# define QEMU_HARDFLOAT_1F32_USE_FP 0
# define QEMU_HARDFLOAT_1F64_USE_FP 1
# define QEMU_HARDFLOAT_2F32_USE_FP 0
# define QEMU_HARDFLOAT_2F64_USE_FP 1
# define QEMU_HARDFLOAT_3F32_USE_FP 0
# define QEMU_HARDFLOAT_3F64_USE_FP 1
#else
# define QEMU_HARDFLOAT_1F32_USE_FP 0
# define QEMU_HARDFLOAT_1F64_USE_FP 0
# define QEMU_HARDFLOAT_2F32_USE_FP 0
# define QEMU_HARDFLOAT_2F64_USE_FP 0
# define QEMU_HARDFLOAT_3F32_USE_FP 0
# define QEMU_HARDFLOAT_3F64_USE_FP 0
#endif
/*
* QEMU_HARDFLOAT_USE_ISINF chooses whether to use isinf() over
* float{32,64}_is_infinity when !USE_FP.
* On x86_64/aarch64, using the former over the latter can yield a ~6% speedup.
* On power64 however, using isinf() reduces fp-bench performance by up to 50%.
*/
#if defined(__x86_64__) || defined(__aarch64__)
# define QEMU_HARDFLOAT_USE_ISINF 1
#else
# define QEMU_HARDFLOAT_USE_ISINF 0
#endif
/*
* Some targets clear the FP flags before most FP operations. This prevents
* the use of hardfloat, since hardfloat relies on the inexact flag being
* already set.
*/
#if defined(TARGET_PPC) || defined(__FAST_MATH__)
# if defined(__FAST_MATH__)
# warning disabling hardfloat due to -ffast-math: hardfloat requires an exact \
IEEE implementation
# endif
# define QEMU_NO_HARDFLOAT 1
# define QEMU_SOFTFLOAT_ATTR QEMU_FLATTEN
#else
# define QEMU_NO_HARDFLOAT 0
# define QEMU_SOFTFLOAT_ATTR QEMU_FLATTEN __attribute__((noinline))
#endif
static inline bool can_use_fpu(const float_status *s)
{
if (QEMU_NO_HARDFLOAT) {
return false;
}
return likely(s->float_exception_flags & float_flag_inexact &&
s->float_rounding_mode == float_round_nearest_even);
}
/*
* Hardfloat generation functions. Each operation can have two flavors:
* either using softfloat primitives (e.g. float32_is_zero_or_normal) for
* most condition checks, or native ones (e.g. fpclassify).
*
* The flavor is chosen by the callers. Instead of using macros, we rely on the
* compiler to propagate constants and inline everything into the callers.
*
* We only generate functions for operations with two inputs, since only
* these are common enough to justify consolidating them into common code.
*/
typedef union {
float32 s;
float h;
} union_float32;
typedef union {
float64 s;
double h;
} union_float64;
typedef bool (*f32_check_fn)(union_float32 a, union_float32 b);
typedef bool (*f64_check_fn)(union_float64 a, union_float64 b);
typedef float32 (*soft_f32_op2_fn)(float32 a, float32 b, float_status *s);
typedef float64 (*soft_f64_op2_fn)(float64 a, float64 b, float_status *s);
typedef float (*hard_f32_op2_fn)(float a, float b);
typedef double (*hard_f64_op2_fn)(double a, double b);
/* 2-input is-zero-or-normal */
static inline bool f32_is_zon2(union_float32 a, union_float32 b)
{
if (QEMU_HARDFLOAT_2F32_USE_FP) {
/*
* Not using a temp variable for consecutive fpclassify calls ends up
* generating faster code.
*/
return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) &&
(fpclassify(b.h) == FP_NORMAL || fpclassify(b.h) == FP_ZERO);
}
return float32_is_zero_or_normal(a.s) &&
float32_is_zero_or_normal(b.s);
}
static inline bool f64_is_zon2(union_float64 a, union_float64 b)
{
if (QEMU_HARDFLOAT_2F64_USE_FP) {
return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) &&
(fpclassify(b.h) == FP_NORMAL || fpclassify(b.h) == FP_ZERO);
}
return float64_is_zero_or_normal(a.s) &&
float64_is_zero_or_normal(b.s);
}
/* 3-input is-zero-or-normal */
static inline
bool f32_is_zon3(union_float32 a, union_float32 b, union_float32 c)
{
if (QEMU_HARDFLOAT_3F32_USE_FP) {
return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) &&
(fpclassify(b.h) == FP_NORMAL || fpclassify(b.h) == FP_ZERO) &&
(fpclassify(c.h) == FP_NORMAL || fpclassify(c.h) == FP_ZERO);
}
return float32_is_zero_or_normal(a.s) &&
float32_is_zero_or_normal(b.s) &&
float32_is_zero_or_normal(c.s);
}
static inline
bool f64_is_zon3(union_float64 a, union_float64 b, union_float64 c)
{
if (QEMU_HARDFLOAT_3F64_USE_FP) {
return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) &&
(fpclassify(b.h) == FP_NORMAL || fpclassify(b.h) == FP_ZERO) &&
(fpclassify(c.h) == FP_NORMAL || fpclassify(c.h) == FP_ZERO);
}
return float64_is_zero_or_normal(a.s) &&
float64_is_zero_or_normal(b.s) &&
float64_is_zero_or_normal(c.s);
}
static inline bool f32_is_inf(union_float32 a)
{
if (QEMU_HARDFLOAT_USE_ISINF) {
return isinf(a.h);
}
return float32_is_infinity(a.s);
}
static inline bool f64_is_inf(union_float64 a)
{
if (QEMU_HARDFLOAT_USE_ISINF) {
return isinf(a.h);
}
return float64_is_infinity(a.s);
}
/* Note: @fast_test and @post can be NULL */
static inline float32
float32_gen2(float32 xa, float32 xb, float_status *s,
hard_f32_op2_fn hard, soft_f32_op2_fn soft,
f32_check_fn pre, f32_check_fn post,
f32_check_fn fast_test, soft_f32_op2_fn fast_op)
{
union_float32 ua, ub, ur;
ua.s = xa;
ub.s = xb;
if (unlikely(!can_use_fpu(s))) {
goto soft;
}
float32_input_flush2(&ua.s, &ub.s, s);
if (unlikely(!pre(ua, ub))) {
goto soft;
}
if (fast_test && fast_test(ua, ub)) {
return fast_op(ua.s, ub.s, s);
}
ur.h = hard(ua.h, ub.h);
if (unlikely(f32_is_inf(ur))) {
s->float_exception_flags |= float_flag_overflow;
} else if (unlikely(fabsf(ur.h) <= FLT_MIN)) {
if (post == NULL || post(ua, ub)) {
goto soft;
}
}
return ur.s;
soft:
return soft(ua.s, ub.s, s);
}
static inline float64
float64_gen2(float64 xa, float64 xb, float_status *s,
hard_f64_op2_fn hard, soft_f64_op2_fn soft,
f64_check_fn pre, f64_check_fn post,
f64_check_fn fast_test, soft_f64_op2_fn fast_op)
{
union_float64 ua, ub, ur;
ua.s = xa;
ub.s = xb;
if (unlikely(!can_use_fpu(s))) {
goto soft;
}
float64_input_flush2(&ua.s, &ub.s, s);
if (unlikely(!pre(ua, ub))) {
goto soft;
}
if (fast_test && fast_test(ua, ub)) {
return fast_op(ua.s, ub.s, s);
}
ur.h = hard(ua.h, ub.h);
if (unlikely(f64_is_inf(ur))) {
s->float_exception_flags |= float_flag_overflow;
} else if (unlikely(fabs(ur.h) <= DBL_MIN)) {
if (post == NULL || post(ua, ub)) {
goto soft;
}
}
return ur.s;
soft:
return soft(ua.s, ub.s, s);
}
/*----------------------------------------------------------------------------
| Returns the fraction bits of the single-precision floating-point value `a'.
*----------------------------------------------------------------------------*/
static inline uint32_t extractFloat32Frac(float32 a)
{
return float32_val(a) & 0x007FFFFF;
}
/*----------------------------------------------------------------------------
| Returns the exponent bits of the single-precision floating-point value `a'.
*----------------------------------------------------------------------------*/
static inline int extractFloat32Exp(float32 a)
{
return (float32_val(a) >> 23) & 0xFF;
}
/*----------------------------------------------------------------------------
| Returns the sign bit of the single-precision floating-point value `a'.
*----------------------------------------------------------------------------*/
static inline flag extractFloat32Sign(float32 a)
{
return float32_val(a) >> 31;
}
/*----------------------------------------------------------------------------
| Returns the fraction bits of the double-precision floating-point value `a'.
*----------------------------------------------------------------------------*/
static inline uint64_t extractFloat64Frac(float64 a)
{
return float64_val(a) & UINT64_C(0x000FFFFFFFFFFFFF);
}
/*----------------------------------------------------------------------------
| Returns the exponent bits of the double-precision floating-point value `a'.
*----------------------------------------------------------------------------*/
static inline int extractFloat64Exp(float64 a)
{
return (float64_val(a) >> 52) & 0x7FF;
}
/*----------------------------------------------------------------------------
| Returns the sign bit of the double-precision floating-point value `a'.
*----------------------------------------------------------------------------*/
static inline flag extractFloat64Sign(float64 a)
{
return float64_val(a) >> 63;
}
/*
* Classify a floating point number. Everything above float_class_qnan
* is a NaN so cls >= float_class_qnan is any NaN.
*/
typedef enum __attribute__ ((__packed__)) {
float_class_unclassified,
float_class_zero,
float_class_normal,
float_class_inf,
float_class_qnan, /* all NaNs from here */
float_class_snan,
} FloatClass;
/* Simple helpers for checking if, or what kind of, NaN we have */
static inline __attribute__((unused)) bool is_nan(FloatClass c)
{
return unlikely(c >= float_class_qnan);
}
static inline __attribute__((unused)) bool is_snan(FloatClass c)
{
return c == float_class_snan;
}
static inline __attribute__((unused)) bool is_qnan(FloatClass c)
{
return c == float_class_qnan;
}
/*
* Structure holding all of the decomposed parts of a float. The
* exponent is unbiased and the fraction is normalized. All
* calculations are done with a 64 bit fraction and then rounded as
* appropriate for the final format.
*
* Thanks to the packed FloatClass a decent compiler should be able to
* fit the whole structure into registers and avoid using the stack
* for parameter passing.
*/
typedef struct {
uint64_t frac;
int32_t exp;
FloatClass cls;
bool sign;
} FloatParts;
#define DECOMPOSED_BINARY_POINT (64 - 2)
#define DECOMPOSED_IMPLICIT_BIT (1ull << DECOMPOSED_BINARY_POINT)
#define DECOMPOSED_OVERFLOW_BIT (DECOMPOSED_IMPLICIT_BIT << 1)
/* Structure holding all of the relevant parameters for a format.
* exp_size: the size of the exponent field
* exp_bias: the offset applied to the exponent field
* exp_max: the maximum normalised exponent
* frac_size: the size of the fraction field
* frac_shift: shift to normalise the fraction with DECOMPOSED_BINARY_POINT
* The following are computed based the size of fraction
* frac_lsb: least significant bit of fraction
* frac_lsbm1: the bit below the least significant bit (for rounding)
* round_mask/roundeven_mask: masks used for rounding
* The following optional modifiers are available:
* arm_althp: handle ARM Alternative Half Precision
*/
typedef struct {
int exp_size;
int exp_bias;
int exp_max;
int frac_size;
int frac_shift;
uint64_t frac_lsb;
uint64_t frac_lsbm1;
uint64_t round_mask;
uint64_t roundeven_mask;
bool arm_althp;
} FloatFmt;
/* Expand fields based on the size of exponent and fraction */
#define FLOAT_PARAMS(E, F) \
.exp_size = E, \
.exp_bias = ((1 << E) - 1) >> 1, \
.exp_max = (1 << E) - 1, \
.frac_size = F, \
.frac_shift = DECOMPOSED_BINARY_POINT - F, \
.frac_lsb = 1ull << (DECOMPOSED_BINARY_POINT - F), \
.frac_lsbm1 = 1ull << ((DECOMPOSED_BINARY_POINT - F) - 1), \
.round_mask = (1ull << (DECOMPOSED_BINARY_POINT - F)) - 1, \
.roundeven_mask = (2ull << (DECOMPOSED_BINARY_POINT - F)) - 1
static const FloatFmt float16_params = {
FLOAT_PARAMS(5, 10)
};
static const FloatFmt float16_params_ahp = {
FLOAT_PARAMS(5, 10),
.arm_althp = true
};
static const FloatFmt float32_params = {
FLOAT_PARAMS(8, 23)
};
static const FloatFmt float64_params = {
FLOAT_PARAMS(11, 52)
};
/* Unpack a float to parts, but do not canonicalize. */
static inline FloatParts unpack_raw(FloatFmt fmt, uint64_t raw)
{
const int sign_pos = fmt.frac_size + fmt.exp_size;
return (FloatParts) {
.cls = float_class_unclassified,
.sign = extract64(raw, sign_pos, 1),
.exp = extract64(raw, fmt.frac_size, fmt.exp_size),
.frac = extract64(raw, 0, fmt.frac_size),
};
}
static inline FloatParts float16_unpack_raw(float16 f)
{
return unpack_raw(float16_params, f);
}
static inline FloatParts float32_unpack_raw(float32 f)
{
return unpack_raw(float32_params, f);
}
static inline FloatParts float64_unpack_raw(float64 f)
{
return unpack_raw(float64_params, f);
}
/* Pack a float from parts, but do not canonicalize. */
static inline uint64_t pack_raw(FloatFmt fmt, FloatParts p)
{
const int sign_pos = fmt.frac_size + fmt.exp_size;
uint64_t ret = deposit64(p.frac, fmt.frac_size, fmt.exp_size, p.exp);
return deposit64(ret, sign_pos, 1, p.sign);
}
static inline float16 float16_pack_raw(FloatParts p)
{
return make_float16(pack_raw(float16_params, p));
}
static inline float32 float32_pack_raw(FloatParts p)
{
return make_float32(pack_raw(float32_params, p));
}
static inline float64 float64_pack_raw(FloatParts p)
{
return make_float64(pack_raw(float64_params, p));
}
/*----------------------------------------------------------------------------
| Functions and definitions to determine: (1) whether tininess for underflow
| is detected before or after rounding by default, (2) what (if anything)
| happens when exceptions are raised, (3) how signaling NaNs are distinguished
| from quiet NaNs, (4) the default generated quiet NaNs, and (5) how NaNs
| are propagated from function inputs to output. These details are target-
| specific.
*----------------------------------------------------------------------------*/
#include "softfloat-specialize.inc.c"
/* Canonicalize EXP and FRAC, setting CLS. */
static FloatParts sf_canonicalize(FloatParts part, const FloatFmt *parm,
float_status *status)
{
if (part.exp == parm->exp_max && !parm->arm_althp) {
if (part.frac == 0) {
part.cls = float_class_inf;
} else {
part.frac <<= parm->frac_shift;
part.cls = (parts_is_snan_frac(part.frac, status)
? float_class_snan : float_class_qnan);
}
} else if (part.exp == 0) {
if (likely(part.frac == 0)) {
part.cls = float_class_zero;
} else if (status->flush_inputs_to_zero) {
float_raise(float_flag_input_denormal, status);
part.cls = float_class_zero;
part.frac = 0;
} else {
int shift = clz64(part.frac) - 1;
part.cls = float_class_normal;
part.exp = parm->frac_shift - parm->exp_bias - shift + 1;
part.frac <<= shift;
}
} else {
part.cls = float_class_normal;
part.exp -= parm->exp_bias;
part.frac = DECOMPOSED_IMPLICIT_BIT + (part.frac << parm->frac_shift);
}
return part;
}
/* Round and uncanonicalize a floating-point number by parts. There
* are FRAC_SHIFT bits that may require rounding at the bottom of the
* fraction; these bits will be removed. The exponent will be biased
* by EXP_BIAS and must be bounded by [EXP_MAX-1, 0].
*/
static FloatParts round_canonical(FloatParts p, float_status *s,
const FloatFmt *parm)
{
const uint64_t frac_lsb = parm->frac_lsb;
const uint64_t frac_lsbm1 = parm->frac_lsbm1;
const uint64_t round_mask = parm->round_mask;
const uint64_t roundeven_mask = parm->roundeven_mask;
const int exp_max = parm->exp_max;
const int frac_shift = parm->frac_shift;
uint64_t frac, inc;
int exp, flags = 0;
bool overflow_norm;
frac = p.frac;
exp = p.exp;
switch (p.cls) {
case float_class_normal:
switch (s->float_rounding_mode) {
case float_round_nearest_even:
overflow_norm = false;
inc = ((frac & roundeven_mask) != frac_lsbm1 ? frac_lsbm1 : 0);
break;
case float_round_ties_away:
overflow_norm = false;
inc = frac_lsbm1;
break;
case float_round_to_zero:
overflow_norm = true;
inc = 0;
break;
case float_round_up:
inc = p.sign ? 0 : round_mask;
overflow_norm = p.sign;
break;
case float_round_down:
inc = p.sign ? round_mask : 0;
overflow_norm = !p.sign;
break;
case float_round_to_odd:
overflow_norm = true;
inc = frac & frac_lsb ? 0 : round_mask;
break;
default:
g_assert_not_reached();
}
exp += parm->exp_bias;
if (likely(exp > 0)) {
if (frac & round_mask) {
flags |= float_flag_inexact;
frac += inc;
if (frac & DECOMPOSED_OVERFLOW_BIT) {
frac >>= 1;
exp++;
}
}
frac >>= frac_shift;
if (parm->arm_althp) {
/* ARM Alt HP eschews Inf and NaN for a wider exponent. */
if (unlikely(exp > exp_max)) {
/* Overflow. Return the maximum normal. */
flags = float_flag_invalid;
exp = exp_max;
frac = -1;
}
} else if (unlikely(exp >= exp_max)) {
flags |= float_flag_overflow | float_flag_inexact;
if (overflow_norm) {
exp = exp_max - 1;
frac = -1;
} else {
p.cls = float_class_inf;
goto do_inf;
}
}
} else if (s->flush_to_zero) {
flags |= float_flag_output_denormal;
p.cls = float_class_zero;
goto do_zero;
} else {
bool is_tiny = (s->float_detect_tininess
== float_tininess_before_rounding)
|| (exp < 0)
|| !((frac + inc) & DECOMPOSED_OVERFLOW_BIT);
shift64RightJamming(frac, 1 - exp, &frac);
if (frac & round_mask) {
/* Need to recompute round-to-even. */
switch (s->float_rounding_mode) {
case float_round_nearest_even:
inc = ((frac & roundeven_mask) != frac_lsbm1
? frac_lsbm1 : 0);
break;
case float_round_to_odd:
inc = frac & frac_lsb ? 0 : round_mask;
break;
}
flags |= float_flag_inexact;
frac += inc;
}
exp = (frac & DECOMPOSED_IMPLICIT_BIT ? 1 : 0);
frac >>= frac_shift;
if (is_tiny && (flags & float_flag_inexact)) {
flags |= float_flag_underflow;
}
if (exp == 0 && frac == 0) {
p.cls = float_class_zero;
}
}
break;
case float_class_zero:
do_zero:
exp = 0;
frac = 0;
break;
case float_class_inf:
do_inf:
assert(!parm->arm_althp);
exp = exp_max;
frac = 0;
break;
case float_class_qnan:
case float_class_snan:
assert(!parm->arm_althp);
exp = exp_max;
frac >>= parm->frac_shift;
break;
default:
g_assert_not_reached();
}
float_raise(flags, s);
p.exp = exp;
p.frac = frac;
return p;
}
/* Explicit FloatFmt version */
static FloatParts float16a_unpack_canonical(float16 f, float_status *s,
const FloatFmt *params)
{
return sf_canonicalize(float16_unpack_raw(f), params, s);
}
static FloatParts float16_unpack_canonical(float16 f, float_status *s)
{
return float16a_unpack_canonical(f, s, &float16_params);
}
static float16 float16a_round_pack_canonical(FloatParts p, float_status *s,
const FloatFmt *params)
{
return float16_pack_raw(round_canonical(p, s, params));
}
static float16 float16_round_pack_canonical(FloatParts p, float_status *s)
{
return float16a_round_pack_canonical(p, s, &float16_params);
}
static FloatParts float32_unpack_canonical(float32 f, float_status *s)
{
return sf_canonicalize(float32_unpack_raw(f), &float32_params, s);
}
static float32 float32_round_pack_canonical(FloatParts p, float_status *s)
{
return float32_pack_raw(round_canonical(p, s, &float32_params));
}
static FloatParts float64_unpack_canonical(float64 f, float_status *s)
{
return sf_canonicalize(float64_unpack_raw(f), &float64_params, s);
}
static float64 float64_round_pack_canonical(FloatParts p, float_status *s)
{
return float64_pack_raw(round_canonical(p, s, &float64_params));
}
static FloatParts return_nan(FloatParts a, float_status *s)
{
switch (a.cls) {
case float_class_snan:
s->float_exception_flags |= float_flag_invalid;
a = parts_silence_nan(a, s);
/* fall through */
case float_class_qnan:
if (s->default_nan_mode) {
return parts_default_nan(s);
}
break;
default:
g_assert_not_reached();
}
return a;
}
static FloatParts pick_nan(FloatParts a, FloatParts b, float_status *s)
{
if (is_snan(a.cls) || is_snan(b.cls)) {
s->float_exception_flags |= float_flag_invalid;
}
if (s->default_nan_mode) {
return parts_default_nan(s);
} else {
if (pickNaN(a.cls, b.cls,
a.frac > b.frac ||
(a.frac == b.frac && a.sign < b.sign))) {
a = b;
}
if (is_snan(a.cls)) {
return parts_silence_nan(a, s);
}
}
return a;
}
static FloatParts pick_nan_muladd(FloatParts a, FloatParts b, FloatParts c,
bool inf_zero, float_status *s)
{
int which;
if (is_snan(a.cls) || is_snan(b.cls) || is_snan(c.cls)) {
s->float_exception_flags |= float_flag_invalid;
}
which = pickNaNMulAdd(a.cls, b.cls, c.cls, inf_zero, s);
if (s->default_nan_mode) {
/* Note that this check is after pickNaNMulAdd so that function
* has an opportunity to set the Invalid flag.
*/
which = 3;
}
switch (which) {
case 0:
break;
case 1:
a = b;
break;
case 2:
a = c;
break;
case 3:
return parts_default_nan(s);
default:
g_assert_not_reached();
}
if (is_snan(a.cls)) {
return parts_silence_nan(a, s);
}
return a;
}
/*
* Returns the result of adding or subtracting the values of the
* floating-point values `a' and `b'. The operation is performed
* according to the IEC/IEEE Standard for Binary Floating-Point
* Arithmetic.
*/
static FloatParts addsub_floats(FloatParts a, FloatParts b, bool subtract,
float_status *s)
{
bool a_sign = a.sign;
bool b_sign = b.sign ^ subtract;
if (a_sign != b_sign) {
/* Subtraction */
if (a.cls == float_class_normal && b.cls == float_class_normal) {
if (a.exp > b.exp || (a.exp == b.exp && a.frac >= b.frac)) {
shift64RightJamming(b.frac, a.exp - b.exp, &b.frac);
a.frac = a.frac - b.frac;
} else {
shift64RightJamming(a.frac, b.exp - a.exp, &a.frac);
a.frac = b.frac - a.frac;
a.exp = b.exp;
a_sign ^= 1;
}
if (a.frac == 0) {
a.cls = float_class_zero;
a.sign = s->float_rounding_mode == float_round_down;
} else {
int shift = clz64(a.frac) - 1;
a.frac = a.frac << shift;
a.exp = a.exp - shift;
a.sign = a_sign;
}
return a;
}
if (is_nan(a.cls) || is_nan(b.cls)) {
return pick_nan(a, b, s);
}
if (a.cls == float_class_inf) {
if (b.cls == float_class_inf) {
float_raise(float_flag_invalid, s);
return parts_default_nan(s);
}
return a;
}
if (a.cls == float_class_zero && b.cls == float_class_zero) {
a.sign = s->float_rounding_mode == float_round_down;
return a;
}
if (a.cls == float_class_zero || b.cls == float_class_inf) {
b.sign = a_sign ^ 1;
return b;
}
if (b.cls == float_class_zero) {
return a;
}
} else {
/* Addition */
if (a.cls == float_class_normal && b.cls == float_class_normal) {
if (a.exp > b.exp) {
shift64RightJamming(b.frac, a.exp - b.exp, &b.frac);
} else if (a.exp < b.exp) {
shift64RightJamming(a.frac, b.exp - a.exp, &a.frac);
a.exp = b.exp;
}
a.frac += b.frac;
if (a.frac & DECOMPOSED_OVERFLOW_BIT) {
shift64RightJamming(a.frac, 1, &a.frac);
a.exp += 1;
}
return a;
}
if (is_nan(a.cls) || is_nan(b.cls)) {
return pick_nan(a, b, s);
}
if (a.cls == float_class_inf || b.cls == float_class_zero) {
return a;
}
if (b.cls == float_class_inf || a.cls == float_class_zero) {
b.sign = b_sign;
return b;
}
}
g_assert_not_reached();
}
/*
* Returns the result of adding or subtracting the floating-point
* values `a' and `b'. The operation is performed according to the
* IEC/IEEE Standard for Binary Floating-Point Arithmetic.
*/
float16 QEMU_FLATTEN float16_add(float16 a, float16 b, float_status *status)
{
FloatParts pa = float16_unpack_canonical(a, status);
FloatParts pb = float16_unpack_canonical(b, status);
FloatParts pr = addsub_floats(pa, pb, false, status);
return float16_round_pack_canonical(pr, status);
}
float16 QEMU_FLATTEN float16_sub(float16 a, float16 b, float_status *status)
{
FloatParts pa = float16_unpack_canonical(a, status);
FloatParts pb = float16_unpack_canonical(b, status);
FloatParts pr = addsub_floats(pa, pb, true, status);
return float16_round_pack_canonical(pr, status);
}
static float32 QEMU_SOFTFLOAT_ATTR
soft_f32_addsub(float32 a, float32 b, bool subtract, float_status *status)
{
FloatParts pa = float32_unpack_canonical(a, status);
FloatParts pb = float32_unpack_canonical(b, status);
FloatParts pr = addsub_floats(pa, pb, subtract, status);
return float32_round_pack_canonical(pr, status);
}
static inline float32 soft_f32_add(float32 a, float32 b, float_status *status)
{
return soft_f32_addsub(a, b, false, status);
}
static inline float32 soft_f32_sub(float32 a, float32 b, float_status *status)
{
return soft_f32_addsub(a, b, true, status);
}
static float64 QEMU_SOFTFLOAT_ATTR
soft_f64_addsub(float64 a, float64 b, bool subtract, float_status *status)
{
FloatParts pa = float64_unpack_canonical(a, status);
FloatParts pb = float64_unpack_canonical(b, status);
FloatParts pr = addsub_floats(pa, pb, subtract, status);
return float64_round_pack_canonical(pr, status);
}
static inline float64 soft_f64_add(float64 a, float64 b, float_status *status)
{
return soft_f64_addsub(a, b, false, status);
}
static inline float64 soft_f64_sub(float64 a, float64 b, float_status *status)
{
return soft_f64_addsub(a, b, true, status);
}
static float hard_f32_add(float a, float b)
{
return a + b;
}
static float hard_f32_sub(float a, float b)
{
return a - b;
}
static double hard_f64_add(double a, double b)
{
return a + b;
}
static double hard_f64_sub(double a, double b)
{
return a - b;
}
static bool f32_addsub_post(union_float32 a, union_float32 b)
{
if (QEMU_HARDFLOAT_2F32_USE_FP) {
return !(fpclassify(a.h) == FP_ZERO && fpclassify(b.h) == FP_ZERO);
}
return !(float32_is_zero(a.s) && float32_is_zero(b.s));
}
static bool f64_addsub_post(union_float64 a, union_float64 b)
{
if (QEMU_HARDFLOAT_2F64_USE_FP) {
return !(fpclassify(a.h) == FP_ZERO && fpclassify(b.h) == FP_ZERO);
} else {
return !(float64_is_zero(a.s) && float64_is_zero(b.s));
}
}
static float32 float32_addsub(float32 a, float32 b, float_status *s,
hard_f32_op2_fn hard, soft_f32_op2_fn soft)
{
return float32_gen2(a, b, s, hard, soft,
f32_is_zon2, f32_addsub_post, NULL, NULL);
}
static float64 float64_addsub(float64 a, float64 b, float_status *s,
hard_f64_op2_fn hard, soft_f64_op2_fn soft)
{
return float64_gen2(a, b, s, hard, soft,
f64_is_zon2, f64_addsub_post, NULL, NULL);
}
float32 QEMU_FLATTEN
float32_add(float32 a, float32 b, float_status *s)
{
return float32_addsub(a, b, s, hard_f32_add, soft_f32_add);
}
float32 QEMU_FLATTEN
float32_sub(float32 a, float32 b, float_status *s)
{
return float32_addsub(a, b, s, hard_f32_sub, soft_f32_sub);
}
float64 QEMU_FLATTEN
float64_add(float64 a, float64 b, float_status *s)
{
return float64_addsub(a, b, s, hard_f64_add, soft_f64_add);
}
float64 QEMU_FLATTEN
float64_sub(float64 a, float64 b, float_status *s)
{
return float64_addsub(a, b, s, hard_f64_sub, soft_f64_sub);
}
/*
* Returns the result of multiplying the floating-point values `a' and
* `b'. The operation is performed according to the IEC/IEEE Standard
* for Binary Floating-Point Arithmetic.
*/
static FloatParts mul_floats(FloatParts a, FloatParts b, float_status *s)
{
bool sign = a.sign ^ b.sign;
if (a.cls == float_class_normal && b.cls == float_class_normal) {
uint64_t hi, lo;
int exp = a.exp + b.exp;
mul64To128(a.frac, b.frac, &hi, &lo);
shift128RightJamming(hi, lo, DECOMPOSED_BINARY_POINT, &hi, &lo);
if (lo & DECOMPOSED_OVERFLOW_BIT) {
shift64RightJamming(lo, 1, &lo);
exp += 1;
}
/* Re-use a */
a.exp = exp;
a.sign = sign;
a.frac = lo;
return a;
}
/* handle all the NaN cases */
if (is_nan(a.cls) || is_nan(b.cls)) {
return pick_nan(a, b, s);
}
/* Inf * Zero == NaN */
if ((a.cls == float_class_inf && b.cls == float_class_zero) ||
(a.cls == float_class_zero && b.cls == float_class_inf)) {
s->float_exception_flags |= float_flag_invalid;
return parts_default_nan(s);
}
/* Multiply by 0 or Inf */
if (a.cls == float_class_inf || a.cls == float_class_zero) {
a.sign = sign;
return a;
}
if (b.cls == float_class_inf || b.cls == float_class_zero) {
b.sign = sign;
return b;
}
g_assert_not_reached();
}
float16 QEMU_FLATTEN float16_mul(float16 a, float16 b, float_status *status)
{
FloatParts pa = float16_unpack_canonical(a, status);
FloatParts pb = float16_unpack_canonical(b, status);
FloatParts pr = mul_floats(pa, pb, status);
return float16_round_pack_canonical(pr, status);
}
static float32 QEMU_SOFTFLOAT_ATTR
soft_f32_mul(float32 a, float32 b, float_status *status)
{
FloatParts pa = float32_unpack_canonical(a, status);
FloatParts pb = float32_unpack_canonical(b, status);
FloatParts pr = mul_floats(pa, pb, status);
return float32_round_pack_canonical(pr, status);
}
static float64 QEMU_SOFTFLOAT_ATTR
soft_f64_mul(float64 a, float64 b, float_status *status)
{
FloatParts pa = float64_unpack_canonical(a, status);
FloatParts pb = float64_unpack_canonical(b, status);
FloatParts pr = mul_floats(pa, pb, status);
return float64_round_pack_canonical(pr, status);
}
static float hard_f32_mul(float a, float b)
{
return a * b;
}
static double hard_f64_mul(double a, double b)
{
return a * b;
}
static bool f32_mul_fast_test(union_float32 a, union_float32 b)
{
return float32_is_zero(a.s) || float32_is_zero(b.s);
}
static bool f64_mul_fast_test(union_float64 a, union_float64 b)
{
return float64_is_zero(a.s) || float64_is_zero(b.s);
}
static float32 f32_mul_fast_op(float32 a, float32 b, float_status *s)
{
bool signbit = float32_is_neg(a) ^ float32_is_neg(b);
return float32_set_sign(float32_zero, signbit);
}
static float64 f64_mul_fast_op(float64 a, float64 b, float_status *s)
{
bool signbit = float64_is_neg(a) ^ float64_is_neg(b);
return float64_set_sign(float64_zero, signbit);
}
float32 QEMU_FLATTEN
float32_mul(float32 a, float32 b, float_status *s)
{
return float32_gen2(a, b, s, hard_f32_mul, soft_f32_mul,
f32_is_zon2, NULL, f32_mul_fast_test, f32_mul_fast_op);
}
float64 QEMU_FLATTEN
float64_mul(float64 a, float64 b, float_status *s)
{
return float64_gen2(a, b, s, hard_f64_mul, soft_f64_mul,
f64_is_zon2, NULL, f64_mul_fast_test, f64_mul_fast_op);
}
/*
* Returns the result of multiplying the floating-point values `a' and
* `b' then adding 'c', with no intermediate rounding step after the
* multiplication. The operation is performed according to the
* IEC/IEEE Standard for Binary Floating-Point Arithmetic 754-2008.
* The flags argument allows the caller to select negation of the
* addend, the intermediate product, or the final result. (The
* difference between this and having the caller do a separate
* negation is that negating externally will flip the sign bit on
* NaNs.)
*/
static FloatParts muladd_floats(FloatParts a, FloatParts b, FloatParts c,
int flags, float_status *s)
{
bool inf_zero = ((1 << a.cls) | (1 << b.cls)) ==
((1 << float_class_inf) | (1 << float_class_zero));
bool p_sign;
bool sign_flip = flags & float_muladd_negate_result;
FloatClass p_class;
uint64_t hi, lo;
int p_exp;
/* It is implementation-defined whether the cases of (0,inf,qnan)
* and (inf,0,qnan) raise InvalidOperation or not (and what QNaN
* they return if they do), so we have to hand this information
* off to the target-specific pick-a-NaN routine.
*/
if (is_nan(a.cls) || is_nan(b.cls) || is_nan(c.cls)) {
return pick_nan_muladd(a, b, c, inf_zero, s);
}
if (inf_zero) {
s->float_exception_flags |= float_flag_invalid;
return parts_default_nan(s);
}
if (flags & float_muladd_negate_c) {
c.sign ^= 1;
}
p_sign = a.sign ^ b.sign;
if (flags & float_muladd_negate_product) {
p_sign ^= 1;
}
if (a.cls == float_class_inf || b.cls == float_class_inf) {
p_class = float_class_inf;
} else if (a.cls == float_class_zero || b.cls == float_class_zero) {
p_class = float_class_zero;
} else {
p_class = float_class_normal;
}
if (c.cls == float_class_inf) {
if (p_class == float_class_inf && p_sign != c.sign) {
s->float_exception_flags |= float_flag_invalid;
return parts_default_nan(s);
} else {
a.cls = float_class_inf;
a.sign = c.sign ^ sign_flip;
return a;
}
}
if (p_class == float_class_inf) {
a.cls = float_class_inf;
a.sign = p_sign ^ sign_flip;
return a;
}
if (p_class == float_class_zero) {
if (c.cls == float_class_zero) {
if (p_sign != c.sign) {
p_sign = s->float_rounding_mode == float_round_down;
}
c.sign = p_sign;
} else if (flags & float_muladd_halve_result) {
c.exp -= 1;
}
c.sign ^= sign_flip;
return c;
}
/* a & b should be normals now... */
assert(a.cls == float_class_normal &&
b.cls == float_class_normal);
p_exp = a.exp + b.exp;
/* Multiply of 2 62-bit numbers produces a (2*62) == 124-bit
* result.
*/
mul64To128(a.frac, b.frac, &hi, &lo);
/* binary point now at bit 124 */
/* check for overflow */
if (hi & (1ULL << (DECOMPOSED_BINARY_POINT * 2 + 1 - 64))) {
shift128RightJamming(hi, lo, 1, &hi, &lo);
p_exp += 1;
}
/* + add/sub */
if (c.cls == float_class_zero) {
/* move binary point back to 62 */
shift128RightJamming(hi, lo, DECOMPOSED_BINARY_POINT, &hi, &lo);
} else {
int exp_diff = p_exp - c.exp;
if (p_sign == c.sign) {
/* Addition */
if (exp_diff <= 0) {
shift128RightJamming(hi, lo,
DECOMPOSED_BINARY_POINT - exp_diff,
&hi, &lo);
lo += c.frac;
p_exp = c.exp;
} else {
uint64_t c_hi, c_lo;
/* shift c to the same binary point as the product (124) */
c_hi = c.frac >> 2;
c_lo = 0;
shift128RightJamming(c_hi, c_lo,
exp_diff,
&c_hi, &c_lo);
add128(hi, lo, c_hi, c_lo, &hi, &lo);
/* move binary point back to 62 */
shift128RightJamming(hi, lo, DECOMPOSED_BINARY_POINT, &hi, &lo);
}
if (lo & DECOMPOSED_OVERFLOW_BIT) {
shift64RightJamming(lo, 1, &lo);
p_exp += 1;
}
} else {
/* Subtraction */
uint64_t c_hi, c_lo;
/* make C binary point match product at bit 124 */
c_hi = c.frac >> 2;
c_lo = 0;
if (exp_diff <= 0) {
shift128RightJamming(hi, lo, -exp_diff, &hi, &lo);
if (exp_diff == 0
&&
(hi > c_hi || (hi == c_hi && lo >= c_lo))) {
sub128(hi, lo, c_hi, c_lo, &hi, &lo);
} else {
sub128(c_hi, c_lo, hi, lo, &hi, &lo);
p_sign ^= 1;
p_exp = c.exp;
}
} else {
shift128RightJamming(c_hi, c_lo,
exp_diff,
&c_hi, &c_lo);
sub128(hi, lo, c_hi, c_lo, &hi, &lo);
}
if (hi == 0 && lo == 0) {
a.cls = float_class_zero;
a.sign = s->float_rounding_mode == float_round_down;
a.sign ^= sign_flip;
return a;
} else {
int shift;
if (hi != 0) {
shift = clz64(hi);
} else {
shift = clz64(lo) + 64;
}
/* Normalizing to a binary point of 124 is the
correct adjust for the exponent. However since we're
shifting, we might as well put the binary point back
at 62 where we really want it. Therefore shift as
if we're leaving 1 bit at the top of the word, but
adjust the exponent as if we're leaving 3 bits. */
shift -= 1;
if (shift >= 64) {
lo = lo << (shift - 64);
} else {
hi = (hi << shift) | (lo >> (64 - shift));
lo = hi | ((lo << shift) != 0);
}
p_exp -= shift - 2;
}
}
}
if (flags & float_muladd_halve_result) {
p_exp -= 1;
}
/* finally prepare our result */
a.cls = float_class_normal;
a.sign = p_sign ^ sign_flip;
a.exp = p_exp;
a.frac = lo;
return a;
}
float16 QEMU_FLATTEN float16_muladd(float16 a, float16 b, float16 c,
int flags, float_status *status)
{
FloatParts pa = float16_unpack_canonical(a, status);
FloatParts pb = float16_unpack_canonical(b, status);
FloatParts pc = float16_unpack_canonical(c, status);
FloatParts pr = muladd_floats(pa, pb, pc, flags, status);
return float16_round_pack_canonical(pr, status);
}
static float32 QEMU_SOFTFLOAT_ATTR
soft_f32_muladd(float32 a, float32 b, float32 c, int flags,
float_status *status)
{
FloatParts pa = float32_unpack_canonical(a, status);
FloatParts pb = float32_unpack_canonical(b, status);
FloatParts pc = float32_unpack_canonical(c, status);
FloatParts pr = muladd_floats(pa, pb, pc, flags, status);
return float32_round_pack_canonical(pr, status);
}
static float64 QEMU_SOFTFLOAT_ATTR
soft_f64_muladd(float64 a, float64 b, float64 c, int flags,
float_status *status)
{
FloatParts pa = float64_unpack_canonical(a, status);
FloatParts pb = float64_unpack_canonical(b, status);
FloatParts pc = float64_unpack_canonical(c, status);
FloatParts pr = muladd_floats(pa, pb, pc, flags, status);
return float64_round_pack_canonical(pr, status);
}
static bool force_soft_fma;
float32 QEMU_FLATTEN
float32_muladd(float32 xa, float32 xb, float32 xc, int flags, float_status *s)
{
union_float32 ua, ub, uc, ur;
ua.s = xa;
ub.s = xb;
uc.s = xc;
if (unlikely(!can_use_fpu(s))) {
goto soft;
}
if (unlikely(flags & float_muladd_halve_result)) {
goto soft;
}
float32_input_flush3(&ua.s, &ub.s, &uc.s, s);
if (unlikely(!f32_is_zon3(ua, ub, uc))) {
goto soft;
}
if (unlikely(force_soft_fma)) {
goto soft;
}
/*
* When (a || b) == 0, there's no need to check for under/over flow,
* since we know the addend is (normal || 0) and the product is 0.
*/
if (float32_is_zero(ua.s) || float32_is_zero(ub.s)) {
union_float32 up;
bool prod_sign;
prod_sign = float32_is_neg(ua.s) ^ float32_is_neg(ub.s);
prod_sign ^= !!(flags & float_muladd_negate_product);
up.s = float32_set_sign(float32_zero, prod_sign);
if (flags & float_muladd_negate_c) {
uc.h = -uc.h;
}
ur.h = up.h + uc.h;
} else {
union_float32 ua_orig = ua;
union_float32 uc_orig = uc;
if (flags & float_muladd_negate_product) {
ua.h = -ua.h;
}
if (flags & float_muladd_negate_c) {
uc.h = -uc.h;
}
ur.h = fmaf(ua.h, ub.h, uc.h);
if (unlikely(f32_is_inf(ur))) {
s->float_exception_flags |= float_flag_overflow;
} else if (unlikely(fabsf(ur.h) <= FLT_MIN)) {
ua = ua_orig;
uc = uc_orig;
goto soft;
}
}
if (flags & float_muladd_negate_result) {
return float32_chs(ur.s);
}
return ur.s;
soft:
return soft_f32_muladd(ua.s, ub.s, uc.s, flags, s);
}
float64 QEMU_FLATTEN
float64_muladd(float64 xa, float64 xb, float64 xc, int flags, float_status *s)
{
union_float64 ua, ub, uc, ur;
ua.s = xa;
ub.s = xb;
uc.s = xc;
if (unlikely(!can_use_fpu(s))) {
goto soft;
}
if (unlikely(flags & float_muladd_halve_result)) {
goto soft;
}
float64_input_flush3(&ua.s, &ub.s, &uc.s, s);
if (unlikely(!f64_is_zon3(ua, ub, uc))) {
goto soft;
}
if (unlikely(force_soft_fma)) {
goto soft;
}
/*
* When (a || b) == 0, there's no need to check for under/over flow,
* since we know the addend is (normal || 0) and the product is 0.
*/
if (float64_is_zero(ua.s) || float64_is_zero(ub.s)) {
union_float64 up;
bool prod_sign;
prod_sign = float64_is_neg(ua.s) ^ float64_is_neg(ub.s);
prod_sign ^= !!(flags & float_muladd_negate_product);
up.s = float64_set_sign(float64_zero, prod_sign);
if (flags & float_muladd_negate_c) {
uc.h = -uc.h;
}
ur.h = up.h + uc.h;
} else {
union_float64 ua_orig = ua;
union_float64 uc_orig = uc;
if (flags & float_muladd_negate_product) {
ua.h = -ua.h;
}
if (flags & float_muladd_negate_c) {
uc.h = -uc.h;
}
ur.h = fma(ua.h, ub.h, uc.h);
if (unlikely(f64_is_inf(ur))) {
s->float_exception_flags |= float_flag_overflow;
} else if (unlikely(fabs(ur.h) <= FLT_MIN)) {
ua = ua_orig;
uc = uc_orig;
goto soft;
}
}
if (flags & float_muladd_negate_result) {
return float64_chs(ur.s);
}
return ur.s;
soft:
return soft_f64_muladd(ua.s, ub.s, uc.s, flags, s);
}
/*
* Returns the result of dividing the floating-point value `a' by the
* corresponding value `b'. The operation is performed according to
* the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
*/
static FloatParts div_floats(FloatParts a, FloatParts b, float_status *s)
{
bool sign = a.sign ^ b.sign;
if (a.cls == float_class_normal && b.cls == float_class_normal) {
uint64_t n0, n1, q, r;
int exp = a.exp - b.exp;
/*
* We want a 2*N / N-bit division to produce exactly an N-bit
* result, so that we do not lose any precision and so that we
* do not have to renormalize afterward. If A.frac < B.frac,
* then division would produce an (N-1)-bit result; shift A left
* by one to produce the an N-bit result, and decrement the
* exponent to match.
*
* The udiv_qrnnd algorithm that we're using requires normalization,
* i.e. the msb of the denominator must be set. Since we know that
* DECOMPOSED_BINARY_POINT is msb-1, the inputs must be shifted left
* by one (more), and the remainder must be shifted right by one.
*/
if (a.frac < b.frac) {
exp -= 1;
shift128Left(0, a.frac, DECOMPOSED_BINARY_POINT + 2, &n1, &n0);
} else {
shift128Left(0, a.frac, DECOMPOSED_BINARY_POINT + 1, &n1, &n0);
}
q = udiv_qrnnd(&r, n1, n0, b.frac << 1);
/*
* Set lsb if there is a remainder, to set inexact.
* As mentioned above, to find the actual value of the remainder we
* would need to shift right, but (1) we are only concerned about
* non-zero-ness, and (2) the remainder will always be even because
* both inputs to the division primitive are even.
*/
a.frac = q | (r != 0);
a.sign = sign;
a.exp = exp;
return a;
}
/* handle all the NaN cases */
if (is_nan(a.cls) || is_nan(b.cls)) {
return pick_nan(a, b, s);
}
/* 0/0 or Inf/Inf */
if (a.cls == b.cls
&&
(a.cls == float_class_inf || a.cls == float_class_zero)) {
s->float_exception_flags |= float_flag_invalid;
return parts_default_nan(s);
}
/* Inf / x or 0 / x */
if (a.cls == float_class_inf || a.cls == float_class_zero) {
a.sign = sign;
return a;
}
/* Div 0 => Inf */
if (b.cls == float_class_zero) {
s->float_exception_flags |= float_flag_divbyzero;
a.cls = float_class_inf;
a.sign = sign;
return a;
}
/* Div by Inf */
if (b.cls == float_class_inf) {
a.cls = float_class_zero;
a.sign = sign;
return a;
}
g_assert_not_reached();
}
float16 float16_div(float16 a, float16 b, float_status *status)
{
FloatParts pa = float16_unpack_canonical(a, status);
FloatParts pb = float16_unpack_canonical(b, status);
FloatParts pr = div_floats(pa, pb, status);
return float16_round_pack_canonical(pr, status);
}
static float32 QEMU_SOFTFLOAT_ATTR
soft_f32_div(float32 a, float32 b, float_status *status)
{
FloatParts pa = float32_unpack_canonical(a, status);
FloatParts pb = float32_unpack_canonical(b, status);
FloatParts pr = div_floats(pa, pb, status);
return float32_round_pack_canonical(pr, status);
}
static float64 QEMU_SOFTFLOAT_ATTR
soft_f64_div(float64 a, float64 b, float_status *status)
{
FloatParts pa = float64_unpack_canonical(a, status);
FloatParts pb = float64_unpack_canonical(b, status);
FloatParts pr = div_floats(pa, pb, status);
return float64_round_pack_canonical(pr, status);
}
static float hard_f32_div(float a, float b)
{
return a / b;
}
static double hard_f64_div(double a, double b)
{
return a / b;
}
static bool f32_div_pre(union_float32 a, union_float32 b)
{
if (QEMU_HARDFLOAT_2F32_USE_FP) {
return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) &&
fpclassify(b.h) == FP_NORMAL;
}
return float32_is_zero_or_normal(a.s) && float32_is_normal(b.s);
}
static bool f64_div_pre(union_float64 a, union_float64 b)
{
if (QEMU_HARDFLOAT_2F64_USE_FP) {
return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) &&
fpclassify(b.h) == FP_NORMAL;
}
return float64_is_zero_or_normal(a.s) && float64_is_normal(b.s);
}
static bool f32_div_post(union_float32 a, union_float32 b)
{
if (QEMU_HARDFLOAT_2F32_USE_FP) {
return fpclassify(a.h) != FP_ZERO;
}
return !float32_is_zero(a.s);
}
static bool f64_div_post(union_float64 a, union_float64 b)
{
if (QEMU_HARDFLOAT_2F64_USE_FP) {
return fpclassify(a.h) != FP_ZERO;
}
return !float64_is_zero(a.s);
}
float32 QEMU_FLATTEN
float32_div(float32 a, float32 b, float_status *s)
{
return float32_gen2(a, b, s, hard_f32_div, soft_f32_div,
f32_div_pre, f32_div_post, NULL, NULL);
}
float64 QEMU_FLATTEN
float64_div(float64 a, float64 b, float_status *s)
{
return float64_gen2(a, b, s, hard_f64_div, soft_f64_div,
f64_div_pre, f64_div_post, NULL, NULL);
}
/*
* Float to Float conversions
*
* Returns the result of converting one float format to another. The
* conversion is performed according to the IEC/IEEE Standard for
* Binary Floating-Point Arithmetic.
*
* The float_to_float helper only needs to take care of raising
* invalid exceptions and handling the conversion on NaNs.
*/
static FloatParts float_to_float(FloatParts a, const FloatFmt *dstf,
float_status *s)
{
if (dstf->arm_althp) {
switch (a.cls) {
case float_class_qnan:
case float_class_snan:
/* There is no NaN in the destination format. Raise Invalid
* and return a zero with the sign of the input NaN.
*/
s->float_exception_flags |= float_flag_invalid;
a.cls = float_class_zero;
a.frac = 0;
a.exp = 0;
break;
case float_class_inf:
/* There is no Inf in the destination format. Raise Invalid
* and return the maximum normal with the correct sign.
*/
s->float_exception_flags |= float_flag_invalid;
a.cls = float_class_normal;
a.exp = dstf->exp_max;
a.frac = ((1ull << dstf->frac_size) - 1) << dstf->frac_shift;
break;
default:
break;
}
} else if (is_nan(a.cls)) {
if (is_snan(a.cls)) {
s->float_exception_flags |= float_flag_invalid;
a = parts_silence_nan(a, s);
}
if (s->default_nan_mode) {
return parts_default_nan(s);
}
}
return a;
}
float32 float16_to_float32(float16 a, bool ieee, float_status *s)
{
const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp;
FloatParts p = float16a_unpack_canonical(a, s, fmt16);
FloatParts pr = float_to_float(p, &float32_params, s);
return float32_round_pack_canonical(pr, s);
}
float64 float16_to_float64(float16 a, bool ieee, float_status *s)
{
const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp;
FloatParts p = float16a_unpack_canonical(a, s, fmt16);
FloatParts pr = float_to_float(p, &float64_params, s);
return float64_round_pack_canonical(pr, s);
}
float16 float32_to_float16(float32 a, bool ieee, float_status *s)
{
const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp;
FloatParts p = float32_unpack_canonical(a, s);
FloatParts pr = float_to_float(p, fmt16, s);
return float16a_round_pack_canonical(pr, s, fmt16);
}
static float64 QEMU_SOFTFLOAT_ATTR
soft_float32_to_float64(float32 a, float_status *s)
{
FloatParts p = float32_unpack_canonical(a, s);
FloatParts pr = float_to_float(p, &float64_params, s);
return float64_round_pack_canonical(pr, s);
}
float64 float32_to_float64(float32 a, float_status *s)
{
if (likely(float32_is_normal(a))) {
/* Widening conversion can never produce inexact results. */
union_float32 uf;
union_float64 ud;
uf.s = a;
ud.h = uf.h;
return ud.s;
} else if (float32_is_zero(a)) {
return float64_set_sign(float64_zero, float32_is_neg(a));
} else {
return soft_float32_to_float64(a, s);
}
}
float16 float64_to_float16(float64 a, bool ieee, float_status *s)
{
const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp;
FloatParts p = float64_unpack_canonical(a, s);
FloatParts pr = float_to_float(p, fmt16, s);
return float16a_round_pack_canonical(pr, s, fmt16);
}
float32 float64_to_float32(float64 a, float_status *s)
{
FloatParts p = float64_unpack_canonical(a, s);
FloatParts pr = float_to_float(p, &float32_params, s);
return float32_round_pack_canonical(pr, s);
}
/*
* Rounds the floating-point value `a' to an integer, and returns the
* result as a floating-point value. The operation is performed
* according to the IEC/IEEE Standard for Binary Floating-Point
* Arithmetic.
*/
static FloatParts round_to_int(FloatParts a, int rmode,
int scale, float_status *s)
{
switch (a.cls) {
case float_class_qnan:
case float_class_snan:
return return_nan(a, s);
case float_class_zero:
case float_class_inf:
/* already "integral" */
break;
case float_class_normal:
scale = MIN(MAX(scale, -0x10000), 0x10000);
a.exp += scale;
if (a.exp >= DECOMPOSED_BINARY_POINT) {
/* already integral */
break;
}
if (a.exp < 0) {
bool one;
/* all fractional */
s->float_exception_flags |= float_flag_inexact;
switch (rmode) {
case float_round_nearest_even:
one = a.exp == -1 && a.frac > DECOMPOSED_IMPLICIT_BIT;
break;
case float_round_ties_away:
one = a.exp == -1 && a.frac >= DECOMPOSED_IMPLICIT_BIT;
break;
case float_round_to_zero:
one = false;
break;
case float_round_up:
one = !a.sign;
break;
case float_round_down:
one = a.sign;
break;
case float_round_to_odd:
one = true;
break;
default:
g_assert_not_reached();
}
if (one) {
a.frac = DECOMPOSED_IMPLICIT_BIT;
a.exp = 0;
} else {
a.cls = float_class_zero;
}
} else {
uint64_t frac_lsb = DECOMPOSED_IMPLICIT_BIT >> a.exp;
uint64_t frac_lsbm1 = frac_lsb >> 1;
uint64_t rnd_even_mask = (frac_lsb - 1) | frac_lsb;
uint64_t rnd_mask = rnd_even_mask >> 1;
uint64_t inc;
switch (rmode) {
case float_round_nearest_even:
inc = ((a.frac & rnd_even_mask) != frac_lsbm1 ? frac_lsbm1 : 0);
break;
case float_round_ties_away:
inc = frac_lsbm1;
break;
case float_round_to_zero:
inc = 0;
break;
case float_round_up:
inc = a.sign ? 0 : rnd_mask;
break;
case float_round_down:
inc = a.sign ? rnd_mask : 0;
break;
case float_round_to_odd:
inc = a.frac & frac_lsb ? 0 : rnd_mask;
break;
default:
g_assert_not_reached();
}
if (a.frac & rnd_mask) {
s->float_exception_flags |= float_flag_inexact;
a.frac += inc;
a.frac &= ~rnd_mask;
if (a.frac & DECOMPOSED_OVERFLOW_BIT) {
a.frac >>= 1;
a.exp++;
}
}
}
break;
default:
g_assert_not_reached();
}
return a;
}
float16 float16_round_to_int(float16 a, float_status *s)
{
FloatParts pa = float16_unpack_canonical(a, s);
FloatParts pr = round_to_int(pa, s->float_rounding_mode, 0, s);
return float16_round_pack_canonical(pr, s);
}
float32 float32_round_to_int(float32 a, float_status *s)
{
FloatParts pa = float32_unpack_canonical(a, s);
FloatParts pr = round_to_int(pa, s->float_rounding_mode, 0, s);
return float32_round_pack_canonical(pr, s);
}
float64 float64_round_to_int(float64 a, float_status *s)
{
FloatParts pa = float64_unpack_canonical(a, s);
FloatParts pr = round_to_int(pa, s->float_rounding_mode, 0, s);
return float64_round_pack_canonical(pr, s);
}
/*
* Returns the result of converting the floating-point value `a' to
* the two's complement integer format. The conversion is performed
* according to the IEC/IEEE Standard for Binary Floating-Point
* Arithmetic---which means in particular that the conversion is
* rounded according to the current rounding mode. If `a' is a NaN,
* the largest positive integer is returned. Otherwise, if the
* conversion overflows, the largest integer with the same sign as `a'
* is returned.
*/
static int64_t round_to_int_and_pack(FloatParts in, int rmode, int scale,
int64_t min, int64_t max,
float_status *s)
{
uint64_t r;
int orig_flags = get_float_exception_flags(s);
FloatParts p = round_to_int(in, rmode, scale, s);
switch (p.cls) {
case float_class_snan:
case float_class_qnan:
s->float_exception_flags = orig_flags | float_flag_invalid;
return max;
case float_class_inf:
s->float_exception_flags = orig_flags | float_flag_invalid;
return p.sign ? min : max;
case float_class_zero:
return 0;
case float_class_normal:
if (p.exp < DECOMPOSED_BINARY_POINT) {
r = p.frac >> (DECOMPOSED_BINARY_POINT - p.exp);
} else if (p.exp - DECOMPOSED_BINARY_POINT < 2) {
r = p.frac << (p.exp - DECOMPOSED_BINARY_POINT);
} else {
r = UINT64_MAX;
}
if (p.sign) {
if (r <= -(uint64_t) min) {
return -r;
} else {
s->float_exception_flags = orig_flags | float_flag_invalid;
return min;
}
} else {
if (r <= max) {
return r;
} else {
s->float_exception_flags = orig_flags | float_flag_invalid;
return max;
}
}
default:
g_assert_not_reached();
}
}
int16_t float16_to_int16_scalbn(float16 a, int rmode, int scale,
float_status *s)
{
return round_to_int_and_pack(float16_unpack_canonical(a, s),
rmode, scale, INT16_MIN, INT16_MAX, s);
}
int32_t float16_to_int32_scalbn(float16 a, int rmode, int scale,
float_status *s)
{
return round_to_int_and_pack(float16_unpack_canonical(a, s),
rmode, scale, INT32_MIN, INT32_MAX, s);
}
int64_t float16_to_int64_scalbn(float16 a, int rmode, int scale,
float_status *s)
{
return round_to_int_and_pack(float16_unpack_canonical(a, s),
rmode, scale, INT64_MIN, INT64_MAX, s);
}
int16_t float32_to_int16_scalbn(float32 a, int rmode, int scale,
float_status *s)
{
return round_to_int_and_pack(float32_unpack_canonical(a, s),
rmode, scale, INT16_MIN, INT16_MAX, s);
}
int32_t float32_to_int32_scalbn(float32 a, int rmode, int scale,
float_status *s)
{
return round_to_int_and_pack(float32_unpack_canonical(a, s),
rmode, scale, INT32_MIN, INT32_MAX, s);
}
int64_t float32_to_int64_scalbn(float32 a, int rmode, int scale,
float_status *s)
{
return round_to_int_and_pack(float32_unpack_canonical(a, s),
rmode, scale, INT64_MIN, INT64_MAX, s);
}
int16_t float64_to_int16_scalbn(float64 a, int rmode, int scale,
float_status *s)
{
return round_to_int_and_pack(float64_unpack_canonical(a, s),
rmode, scale, INT16_MIN, INT16_MAX, s);
}
int32_t float64_to_int32_scalbn(float64 a, int rmode, int scale,
float_status *s)
{
return round_to_int_and_pack(float64_unpack_canonical(a, s),
rmode, scale, INT32_MIN, INT32_MAX, s);
}
int64_t float64_to_int64_scalbn(float64 a, int rmode, int scale,
float_status *s)
{
return round_to_int_and_pack(float64_unpack_canonical(a, s),
rmode, scale, INT64_MIN, INT64_MAX, s);
}
int16_t float16_to_int16(float16 a, float_status *s)
{
return float16_to_int16_scalbn(a, s->float_rounding_mode, 0, s);
}
int32_t float16_to_int32(float16 a, float_status *s)
{
return float16_to_int32_scalbn(a, s->float_rounding_mode, 0, s);
}
int64_t float16_to_int64(float16 a, float_status *s)
{
return float16_to_int64_scalbn(a, s->float_rounding_mode, 0, s);
}
int16_t float32_to_int16(float32 a, float_status *s)
{
return float32_to_int16_scalbn(a, s->float_rounding_mode, 0, s);
}
int32_t float32_to_int32(float32 a, float_status *s)
{
return float32_to_int32_scalbn(a, s->float_rounding_mode, 0, s);
}
int64_t float32_to_int64(float32 a, float_status *s)
{
return float32_to_int64_scalbn(a, s->float_rounding_mode, 0, s);
}
int16_t float64_to_int16(float64 a, float_status *s)
{
return float64_to_int16_scalbn(a, s->float_rounding_mode, 0, s);
}
int32_t float64_to_int32(float64 a, float_status *s)
{
return float64_to_int32_scalbn(a, s->float_rounding_mode, 0, s);
}
int64_t float64_to_int64(float64 a, float_status *s)
{
return float64_to_int64_scalbn(a, s->float_rounding_mode, 0, s);
}
int16_t float16_to_int16_round_to_zero(float16 a, float_status *s)
{
return float16_to_int16_scalbn(a, float_round_to_zero, 0, s);
}
int32_t float16_to_int32_round_to_zero(float16 a, float_status *s)
{
return float16_to_int32_scalbn(a, float_round_to_zero, 0, s);
}
int64_t float16_to_int64_round_to_zero(float16 a, float_status *s)
{
return float16_to_int64_scalbn(a, float_round_to_zero, 0, s);
}
int16_t float32_to_int16_round_to_zero(float32 a, float_status *s)
{
return float32_to_int16_scalbn(a, float_round_to_zero, 0, s);
}
int32_t float32_to_int32_round_to_zero(float32 a, float_status *s)
{
return float32_to_int32_scalbn(a, float_round_to_zero, 0, s);
}
int64_t float32_to_int64_round_to_zero(float32 a, float_status *s)
{
return float32_to_int64_scalbn(a, float_round_to_zero, 0, s);
}
int16_t float64_to_int16_round_to_zero(float64 a, float_status *s)
{
return float64_to_int16_scalbn(a, float_round_to_zero, 0, s);
}
int32_t float64_to_int32_round_to_zero(float64 a, float_status *s)
{
return float64_to_int32_scalbn(a, float_round_to_zero, 0, s);
}
int64_t float64_to_int64_round_to_zero(float64 a, float_status *s)
{
return float64_to_int64_scalbn(a, float_round_to_zero, 0, s);
}
/*
* Returns the result of converting the floating-point value `a' to
* the unsigned integer format. The conversion is performed according
* to the IEC/IEEE Standard for Binary Floating-Point
* Arithmetic---which means in particular that the conversion is
* rounded according to the current rounding mode. If `a' is a NaN,
* the largest unsigned integer is returned. Otherwise, if the
* conversion overflows, the largest unsigned integer is returned. If
* the 'a' is negative, the result is rounded and zero is returned;
* values that do not round to zero will raise the inexact exception
* flag.
*/
static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, int scale,
uint64_t max, float_status *s)
{
int orig_flags = get_float_exception_flags(s);
FloatParts p = round_to_int(in, rmode, scale, s);
uint64_t r;
switch (p.cls) {
case float_class_snan:
case float_class_qnan:
s->float_exception_flags = orig_flags | float_flag_invalid;
return max;
case float_class_inf:
s->float_exception_flags = orig_flags | float_flag_invalid;
return p.sign ? 0 : max;
case float_class_zero:
return 0;
case float_class_normal:
if (p.sign) {
s->float_exception_flags = orig_flags | float_flag_invalid;
return 0;
}
if (p.exp < DECOMPOSED_BINARY_POINT) {
r = p.frac >> (DECOMPOSED_BINARY_POINT - p.exp);
} else if (p.exp - DECOMPOSED_BINARY_POINT < 2) {
r = p.frac << (p.exp - DECOMPOSED_BINARY_POINT);
} else {
s->float_exception_flags = orig_flags | float_flag_invalid;
return max;
}
/* For uint64 this will never trip, but if p.exp is too large
* to shift a decomposed fraction we shall have exited via the
* 3rd leg above.
*/
if (r > max) {
s->float_exception_flags = orig_flags | float_flag_invalid;
return max;
}
return r;
default:
g_assert_not_reached();
}
}
uint16_t float16_to_uint16_scalbn(float16 a, int rmode, int scale,
float_status *s)
{
return round_to_uint_and_pack(float16_unpack_canonical(a, s),
rmode, scale, UINT16_MAX, s);
}
uint32_t float16_to_uint32_scalbn(float16 a, int rmode, int scale,
float_status *s)
{
return round_to_uint_and_pack(float16_unpack_canonical(a, s),
rmode, scale, UINT32_MAX, s);
}
uint64_t float16_to_uint64_scalbn(float16 a, int rmode, int scale,
float_status *s)
{
return round_to_uint_and_pack(float16_unpack_canonical(a, s),
rmode, scale, UINT64_MAX, s);
}
uint16_t float32_to_uint16_scalbn(float32 a, int rmode, int scale,
float_status *s)
{
return round_to_uint_and_pack(float32_unpack_canonical(a, s),
rmode, scale, UINT16_MAX, s);
}
uint32_t float32_to_uint32_scalbn(float32 a, int rmode, int scale,
float_status *s)
{
return round_to_uint_and_pack(float32_unpack_canonical(a, s),
rmode, scale, UINT32_MAX, s);
}
uint64_t float32_to_uint64_scalbn(float32 a, int rmode, int scale,
float_status *s)
{
return round_to_uint_and_pack(float32_unpack_canonical(a, s),
rmode, scale, UINT64_MAX, s);
}
uint16_t float64_to_uint16_scalbn(float64 a, int rmode, int scale,
float_status *s)
{
return round_to_uint_and_pack(float64_unpack_canonical(a, s),
rmode, scale, UINT16_MAX, s);
}
uint32_t float64_to_uint32_scalbn(float64 a, int rmode, int scale,
float_status *s)
{
return round_to_uint_and_pack(float64_unpack_canonical(a, s),
rmode, scale, UINT32_MAX, s);
}
uint64_t float64_to_uint64_scalbn(float64 a, int rmode, int scale,
float_status *s)
{
return round_to_uint_and_pack(float64_unpack_canonical(a, s),
rmode, scale, UINT64_MAX, s);
}
uint16_t float16_to_uint16(float16 a, float_status *s)
{
return float16_to_uint16_scalbn(a, s->float_rounding_mode, 0, s);
}
uint32_t float16_to_uint32(float16 a, float_status *s)
{
return float16_to_uint32_scalbn(a, s->float_rounding_mode, 0, s);
}
uint64_t float16_to_uint64(float16 a, float_status *s)
{
return float16_to_uint64_scalbn(a, s->float_rounding_mode, 0, s);
}
uint16_t float32_to_uint16(float32 a, float_status *s)
{
return float32_to_uint16_scalbn(a, s->float_rounding_mode, 0, s);
}
uint32_t float32_to_uint32(float32 a, float_status *s)
{
return float32_to_uint32_scalbn(a, s->float_rounding_mode, 0, s);
}
uint64_t float32_to_uint64(float32 a, float_status *s)
{
return float32_to_uint64_scalbn(a, s->float_rounding_mode, 0, s);
}
uint16_t float64_to_uint16(float64 a, float_status *s)
{
return float64_to_uint16_scalbn(a, s->float_rounding_mode, 0, s);
}
uint32_t float64_to_uint32(float64 a, float_status *s)
{
return float64_to_uint32_scalbn(a, s->float_rounding_mode, 0, s);
}
uint64_t float64_to_uint64(float64 a, float_status *s)
{
return float64_to_uint64_scalbn(a, s->float_rounding_mode, 0, s);
}
uint16_t float16_to_uint16_round_to_zero(float16 a, float_status *s)
{
return float16_to_uint16_scalbn(a, float_round_to_zero, 0, s);
}
uint32_t float16_to_uint32_round_to_zero(float16 a, float_status *s)
{
return float16_to_uint32_scalbn(a, float_round_to_zero, 0, s);
}
uint64_t float16_to_uint64_round_to_zero(float16 a, float_status *s)
{
return float16_to_uint64_scalbn(a, float_round_to_zero, 0, s);
}
uint16_t float32_to_uint16_round_to_zero(float32 a, float_status *s)
{
return float32_to_uint16_scalbn(a, float_round_to_zero, 0, s);
}
uint32_t float32_to_uint32_round_to_zero(float32 a, float_status *s)
{
return float32_to_uint32_scalbn(a, float_round_to_zero, 0, s);
}
uint64_t float32_to_uint64_round_to_zero(float32 a, float_status *s)
{
return float32_to_uint64_scalbn(a, float_round_to_zero, 0, s);
}
uint16_t float64_to_uint16_round_to_zero(float64 a, float_status *s)
{
return float64_to_uint16_scalbn(a, float_round_to_zero, 0, s);
}
uint32_t float64_to_uint32_round_to_zero(float64 a, float_status *s)
{
return float64_to_uint32_scalbn(a, float_round_to_zero, 0, s);
}
uint64_t float64_to_uint64_round_to_zero(float64 a, float_status *s)
{
return float64_to_uint64_scalbn(a, float_round_to_zero, 0, s);
}
/*
* Integer to float conversions
*
* Returns the result of converting the two's complement integer `a'
* to the floating-point format. The conversion is performed according
* to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
*/
static FloatParts int_to_float(int64_t a, int scale, float_status *status)
{
FloatParts r = { .sign = false };
if (a == 0) {
r.cls = float_class_zero;
} else {
uint64_t f = a;
int shift;
r.cls = float_class_normal;
if (a < 0) {
f = -f;
r.sign = true;
}
shift = clz64(f) - 1;
scale = MIN(MAX(scale, -0x10000), 0x10000);
r.exp = DECOMPOSED_BINARY_POINT - shift + scale;
r.frac = (shift < 0 ? DECOMPOSED_IMPLICIT_BIT : f << shift);
}
return r;
}
float16 int64_to_float16_scalbn(int64_t a, int scale, float_status *status)
{
FloatParts pa = int_to_float(a, scale, status);
return float16_round_pack_canonical(pa, status);
}
float16 int32_to_float16_scalbn(int32_t a, int scale, float_status *status)
{
return int64_to_float16_scalbn(a, scale, status);
}
float16 int16_to_float16_scalbn(int16_t a, int scale, float_status *status)
{
return int64_to_float16_scalbn(a, scale, status);
}
float16 int64_to_float16(int64_t a, float_status *status)
{
return int64_to_float16_scalbn(a, 0, status);
}
float16 int32_to_float16(int32_t a, float_status *status)
{
return int64_to_float16_scalbn(a, 0, status);
}
float16 int16_to_float16(int16_t a, float_status *status)
{
return int64_to_float16_scalbn(a, 0, status);
}
float32 int64_to_float32_scalbn(int64_t a, int scale, float_status *status)
{
FloatParts pa = int_to_float(a, scale, status);
return float32_round_pack_canonical(pa, status);
}
float32 int32_to_float32_scalbn(int32_t a, int scale, float_status *status)
{
return int64_to_float32_scalbn(a, scale, status);
}
float32 int16_to_float32_scalbn(int16_t a, int scale, float_status *status)
{
return int64_to_float32_scalbn(a, scale, status);
}
float32 int64_to_float32(int64_t a, float_status *status)
{
return int64_to_float32_scalbn(a, 0, status);
}
float32 int32_to_float32(int32_t a, float_status *status)
{
return int64_to_float32_scalbn(a, 0, status);
}
float32 int16_to_float32(int16_t a, float_status *status)
{
return int64_to_float32_scalbn(a, 0, status);
}
float64 int64_to_float64_scalbn(int64_t a, int scale, float_status *status)
{
FloatParts pa = int_to_float(a, scale, status);
return float64_round_pack_canonical(pa, status);
}
float64 int32_to_float64_scalbn(int32_t a, int scale, float_status *status)
{
return int64_to_float64_scalbn(a, scale, status);
}
float64 int16_to_float64_scalbn(int16_t a, int scale, float_status *status)
{
return int64_to_float64_scalbn(a, scale, status);
}
float64 int64_to_float64(int64_t a, float_status *status)
{
return int64_to_float64_scalbn(a, 0, status);
}
float64 int32_to_float64(int32_t a, float_status *status)
{
return int64_to_float64_scalbn(a, 0, status);
}
float64 int16_to_float64(int16_t a, float_status *status)
{
return int64_to_float64_scalbn(a, 0, status);
}
/*
* Unsigned Integer to float conversions
*
* Returns the result of converting the unsigned integer `a' to the
* floating-point format. The conversion is performed according to the
* IEC/IEEE Standard for Binary Floating-Point Arithmetic.
*/
static FloatParts uint_to_float(uint64_t a, int scale, float_status *status)
{
FloatParts r = { .sign = false };
if (a == 0) {
r.cls = float_class_zero;
} else {
scale = MIN(MAX(scale, -0x10000), 0x10000);
r.cls = float_class_normal;
if ((int64_t)a < 0) {
r.exp = DECOMPOSED_BINARY_POINT + 1 + scale;
shift64RightJamming(a, 1, &a);
r.frac = a;
} else {
int shift = clz64(a) - 1;
r.exp = DECOMPOSED_BINARY_POINT - shift + scale;
r.frac = a << shift;
}
}
return r;
}
float16 uint64_to_float16_scalbn(uint64_t a, int scale, float_status *status)
{
FloatParts pa = uint_to_float(a, scale, status);
return float16_round_pack_canonical(pa, status);
}
float16 uint32_to_float16_scalbn(uint32_t a, int scale, float_status *status)
{
return uint64_to_float16_scalbn(a, scale, status);
}
float16 uint16_to_float16_scalbn(uint16_t a, int scale, float_status *status)
{
return uint64_to_float16_scalbn(a, scale, status);
}