blob: 615e4fb153ece582e6d50688bf21aa57d8136a1d [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 "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"
/*----------------------------------------------------------------------------
| 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.h"
/*----------------------------------------------------------------------------
| Returns the fraction bits of the half-precision floating-point value `a'.
*----------------------------------------------------------------------------*/
static inline uint32_t extractFloat16Frac(float16 a)
{
return float16_val(a) & 0x3ff;
}
/*----------------------------------------------------------------------------
| Returns the exponent bits of the half-precision floating-point value `a'.
*----------------------------------------------------------------------------*/
static inline int extractFloat16Exp(float16 a)
{
return (float16_val(a) >> 10) & 0x1f;
}
/*----------------------------------------------------------------------------
| Returns the sign bit of the single-precision floating-point value `a'.
*----------------------------------------------------------------------------*/
static inline flag extractFloat16Sign(float16 a)
{
return float16_val(a)>>15;
}
/*----------------------------------------------------------------------------
| 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) & LIT64(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.
*/
/* BUG: 119800861 - bug in mingw gcc makes it so
* using the packed attribute here causes memory corruption
* in int64_to_float64 */
// typedef enum __attribute__ ((__packed__)) {
typedef enum {
float_class_unclassified,
float_class_zero,
float_class_normal,
float_class_inf,
float_class_qnan, /* all NaNs from here */
float_class_snan,
float_class_dnan,
float_class_msnan, /* maybe silenced */
} FloatClass;
/*
* 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
* fram_lsbm1: the bit bellow the least significant bit (for rounding)
* round_mask/roundeven_mask: masks used for rounding
*/
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;
} 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 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));
}
/* Canonicalize EXP and FRAC, setting CLS. */
static FloatParts canonicalize(FloatParts part, const FloatFmt *parm,
float_status *status)
{
if (part.exp == parm->exp_max) {
if (part.frac == 0) {
part.cls = float_class_inf;
} else {
#ifdef NO_SIGNALING_NANS
part.cls = float_class_qnan;
#else
int64_t msb = part.frac << (parm->frac_shift + 2);
if ((msb < 0) == status->snan_bit_is_one) {
part.cls = float_class_snan;
} else {
part.cls = float_class_qnan;
}
#endif
}
} 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_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;
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 (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. */
if (s->float_rounding_mode == float_round_nearest_even) {
inc = ((frac & roundeven_mask) != frac_lsbm1
? frac_lsbm1 : 0);
}
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:
exp = exp_max;
frac = 0;
break;
case float_class_qnan:
case float_class_snan:
exp = exp_max;
break;
default:
g_assert_not_reached();
}
float_raise(flags, s);
p.exp = exp;
p.frac = frac;
return p;
}
static FloatParts float16_unpack_canonical(float16 f, float_status *s)
{
return canonicalize(float16_unpack_raw(f), &float16_params, s);
}
static float16 float16_round_pack_canonical(FloatParts p, float_status *s)
{
switch (p.cls) {
case float_class_dnan:
return float16_default_nan(s);
case float_class_msnan:
return float16_maybe_silence_nan(float16_pack_raw(p), s);
default:
p = round_canonical(p, s, &float16_params);
return float16_pack_raw(p);
}
}
static FloatParts float32_unpack_canonical(float32 f, float_status *s)
{
return canonicalize(float32_unpack_raw(f), &float32_params, s);
}
static float32 float32_round_pack_canonical(FloatParts p, float_status *s)
{
switch (p.cls) {
case float_class_dnan:
return float32_default_nan(s);
case float_class_msnan:
return float32_maybe_silence_nan(float32_pack_raw(p), s);
default:
p = round_canonical(p, s, &float32_params);
return float32_pack_raw(p);
}
}
static FloatParts float64_unpack_canonical(float64 f, float_status *s)
{
return canonicalize(float64_unpack_raw(f), &float64_params, s);
}
static float64 float64_round_pack_canonical(FloatParts p, float_status *s)
{
switch (p.cls) {
case float_class_dnan:
return float64_default_nan(s);
case float_class_msnan:
return float64_maybe_silence_nan(float64_pack_raw(p), s);
default:
p = round_canonical(p, s, &float64_params);
return float64_pack_raw(p);
}
}
/* Simple helpers for checking if what NaN we have */
static bool is_nan(FloatClass c)
{
return unlikely(c >= float_class_qnan);
}
static bool is_snan(FloatClass c)
{
return c == float_class_snan;
}
static bool is_qnan(FloatClass c)
{
return c == float_class_qnan;
}
static FloatParts return_nan(FloatParts a, float_status *s)
{
switch (a.cls) {
case float_class_snan:
s->float_exception_flags |= float_flag_invalid;
a.cls = float_class_msnan;
/* fall through */
case float_class_qnan:
if (s->default_nan_mode) {
a.cls = float_class_dnan;
}
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) {
a.cls = float_class_dnan;
} else {
if (pickNaN(is_qnan(a.cls), is_snan(a.cls),
is_qnan(b.cls), is_snan(b.cls),
a.frac > b.frac ||
(a.frac == b.frac && a.sign < b.sign))) {
a = b;
}
a.cls = float_class_msnan;
}
return a;
}
static FloatParts pick_nan_muladd(FloatParts a, FloatParts b, FloatParts c,
bool inf_zero, float_status *s)
{
if (is_snan(a.cls) || is_snan(b.cls) || is_snan(c.cls)) {
s->float_exception_flags |= float_flag_invalid;
}
if (s->default_nan_mode) {
a.cls = float_class_dnan;
} else {
switch (pickNaNMulAdd(is_qnan(a.cls), is_snan(a.cls),
is_qnan(b.cls), is_snan(b.cls),
is_qnan(c.cls), is_snan(c.cls),
inf_zero, s)) {
case 0:
break;
case 1:
a = b;
break;
case 2:
a = c;
break;
case 3:
a.cls = float_class_dnan;
return a;
default:
g_assert_not_reached();
}
a.cls = float_class_msnan;
}
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);
a.cls = float_class_dnan;
}
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) {
a.frac >>= 1;
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 __attribute__((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);
}
float32 __attribute__((flatten)) float32_add(float32 a, float32 b,
float_status *status)
{
FloatParts pa = float32_unpack_canonical(a, status);
FloatParts pb = float32_unpack_canonical(b, status);
FloatParts pr = addsub_floats(pa, pb, false, status);
return float32_round_pack_canonical(pr, status);
}
float64 __attribute__((flatten)) float64_add(float64 a, float64 b,
float_status *status)
{
FloatParts pa = float64_unpack_canonical(a, status);
FloatParts pb = float64_unpack_canonical(b, status);
FloatParts pr = addsub_floats(pa, pb, false, status);
return float64_round_pack_canonical(pr, status);
}
float16 __attribute__((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);
}
float32 __attribute__((flatten)) float32_sub(float32 a, float32 b,
float_status *status)
{
FloatParts pa = float32_unpack_canonical(a, status);
FloatParts pb = float32_unpack_canonical(b, status);
FloatParts pr = addsub_floats(pa, pb, true, status);
return float32_round_pack_canonical(pr, status);
}
float64 __attribute__((flatten)) float64_sub(float64 a, float64 b,
float_status *status)
{
FloatParts pa = float64_unpack_canonical(a, status);
FloatParts pb = float64_unpack_canonical(b, status);
FloatParts pr = addsub_floats(pa, pb, true, status);
return float64_round_pack_canonical(pr, status);
}
/*
* 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;
a.cls = float_class_dnan;
a.sign = sign;
return a;
}
/* 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 __attribute__((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);
}
float32 __attribute__((flatten)) float32_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);
}
float64 __attribute__((flatten)) float64_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);
}
/*
* 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;
a.cls = float_class_dnan;
return a;
}
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;
a.cls = float_class_dnan;
} 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 __attribute__((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);
}
float32 __attribute__((flatten)) float32_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);
}
float64 __attribute__((flatten)) float64_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);
}
/*
* 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 temp_lo, temp_hi;
int exp = a.exp - b.exp;
if (a.frac < b.frac) {
exp -= 1;
shortShift128Left(0, a.frac, DECOMPOSED_BINARY_POINT + 1,
&temp_hi, &temp_lo);
} else {
shortShift128Left(0, a.frac, DECOMPOSED_BINARY_POINT,
&temp_hi, &temp_lo);
}
/* LSB of quot is set if inexact which roundandpack will use
* to set flags. Yet again we re-use a for the result */
a.frac = div128To64(temp_lo, temp_hi, b.frac);
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;
a.cls = float_class_dnan;
return a;
}
/* 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);
}
float32 float32_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);
}
float64 float64_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);
}
/*
* 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 rounding_mode, float_status *s)
{
if (is_nan(a.cls)) {
return return_nan(a, s);
}
switch (a.cls) {
case float_class_zero:
case float_class_inf:
case float_class_qnan:
/* already "integral" */
break;
case float_class_normal:
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 (rounding_mode) {
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;
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 (rounding_mode) {
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;
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, 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, 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, s);
return float64_round_pack_canonical(pr, s);
}
float64 float64_trunc_to_int(float64 a, float_status *s)
{
FloatParts pa = float64_unpack_canonical(a, s);
FloatParts pr = round_to_int(pa, float_round_to_zero, 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,
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, s);
switch (p.cls) {
case float_class_snan:
case float_class_qnan:
case float_class_dnan:
case float_class_msnan:
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();
}
}
#define FLOAT_TO_INT(fsz, isz) \
int ## isz ## _t float ## fsz ## _to_int ## isz(float ## fsz a, \
float_status *s) \
{ \
FloatParts p = float ## fsz ## _unpack_canonical(a, s); \
return round_to_int_and_pack(p, s->float_rounding_mode, \
INT ## isz ## _MIN, INT ## isz ## _MAX,\
s); \
} \
\
int ## isz ## _t float ## fsz ## _to_int ## isz ## _round_to_zero \
(float ## fsz a, float_status *s) \
{ \
FloatParts p = float ## fsz ## _unpack_canonical(a, s); \
return round_to_int_and_pack(p, float_round_to_zero, \
INT ## isz ## _MIN, INT ## isz ## _MAX,\
s); \
}
FLOAT_TO_INT(16, 16)
FLOAT_TO_INT(16, 32)
FLOAT_TO_INT(16, 64)
FLOAT_TO_INT(32, 16)
FLOAT_TO_INT(32, 32)
FLOAT_TO_INT(32, 64)
FLOAT_TO_INT(64, 16)
FLOAT_TO_INT(64, 32)
FLOAT_TO_INT(64, 64)
#undef FLOAT_TO_INT
/*
* 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, uint64_t max,
float_status *s)
{
int orig_flags = get_float_exception_flags(s);
FloatParts p = round_to_int(in, rmode, s);
switch (p.cls) {
case float_class_snan:
case float_class_qnan:
case float_class_dnan:
case float_class_msnan:
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:
{
uint64_t r;
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;
} else {
return r;
}
}
default:
g_assert_not_reached();
}
}
#define FLOAT_TO_UINT(fsz, isz) \
uint ## isz ## _t float ## fsz ## _to_uint ## isz(float ## fsz a, \
float_status *s) \
{ \
FloatParts p = float ## fsz ## _unpack_canonical(a, s); \
return round_to_uint_and_pack(p, s->float_rounding_mode, \
UINT ## isz ## _MAX, s); \
} \
\
uint ## isz ## _t float ## fsz ## _to_uint ## isz ## _round_to_zero \
(float ## fsz a, float_status *s) \
{ \
FloatParts p = float ## fsz ## _unpack_canonical(a, s); \
return round_to_uint_and_pack(p, float_round_to_zero, \
UINT ## isz ## _MAX, s); \
}
FLOAT_TO_UINT(16, 16)
FLOAT_TO_UINT(16, 32)
FLOAT_TO_UINT(16, 64)
FLOAT_TO_UINT(32, 16)
FLOAT_TO_UINT(32, 32)
FLOAT_TO_UINT(32, 64)
FLOAT_TO_UINT(64, 16)
FLOAT_TO_UINT(64, 32)
FLOAT_TO_UINT(64, 64)
#undef FLOAT_TO_UINT
/*
* 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, float_status *status)
{
FloatParts r;
if (a == 0) {
r.cls = float_class_zero;
r.sign = false;
} else if (a == (1ULL << 63)) {
r.cls = float_class_normal;
r.sign = true;
r.frac = DECOMPOSED_IMPLICIT_BIT;
r.exp = 63;
} else {
uint64_t f;
if (a < 0) {
f = -a;
r.sign = true;
} else {
f = a;
r.sign = false;
}
int shift = clz64(f) - 1;
r.cls = float_class_normal;
r.exp = (DECOMPOSED_BINARY_POINT - shift);
r.frac = f << shift;
}
return r;
}
float16 int64_to_float16(int64_t a, float_status *status)
{
FloatParts pa = int_to_float(a, status);
return float16_round_pack_canonical(pa, status);
}
float16 int32_to_float16(int32_t a, float_status *status)
{
return int64_to_float16(a, status);
}
float16 int16_to_float16(int16_t a, float_status *status)
{
return int64_to_float16(a, status);
}
float32 int64_to_float32(int64_t a, float_status *status)
{
FloatParts pa = int_to_float(a, status);
return float32_round_pack_canonical(pa, status);
}
float32 int32_to_float32(int32_t a, float_status *status)
{
return int64_to_float32(a, status);
}
float32 int16_to_float32(int16_t a, float_status *status)
{
return int64_to_float32(a, status);
}
float64 int64_to_float64(int64_t a, float_status *status)
{
FloatParts pa = int_to_float(a, status);
return float64_round_pack_canonical(pa, status);
}
float64 int32_to_float64(int32_t a, float_status *status)
{
return int64_to_float64(a, status);
}
float64 int16_to_float64(int16_t a, float_status *status)
{
return int64_to_float64(a, 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, float_status *status)
{
FloatParts r = { .sign = false};
if (a == 0) {
r.cls = float_class_zero;
} else {
int spare_bits = clz64(a) - 1;
r.cls = float_class_normal;
r.exp = DECOMPOSED_BINARY_POINT - spare_bits;
if (spare_bits < 0) {
shift64RightJamming(a, -spare_bits, &a);
r.frac = a;
} else {
r.frac = a << spare_bits;
}
}
return r;
}
float16 uint64_to_float16(uint64_t a, float_status *status)
{
FloatParts pa = uint_to_float(a, status);
return float16_round_pack_canonical(pa, status);
}
float16 uint32_to_float16(uint32_t a, float_status *status)
{
return uint64_to_float16(a, status);
}
float16 uint16_to_float16(uint16_t a, float_status *status)
{
return uint64_to_float16(a, status);
}
float32 uint64_to_float32(uint64_t a, float_status *status)
{
FloatParts pa = uint_to_float(a, status);
return float32_round_pack_canonical(pa, status);
}
float32 uint32_to_float32(uint32_t a, float_status *status)
{
return uint64_to_float32(a, status);
}
float32 uint16_to_float32(uint16_t a, float_status *status)
{
return uint64_to_float32(a, status);
}
float64 uint64_to_float64(uint64_t a, float_status *status)
{
FloatParts pa = uint_to_float(a, status);
return float64_round_pack_canonical(pa, status);
}
float64 uint32_to_float64(uint32_t a, float_status *status)
{
return uint64_to_float64(a, status);
}
float64 uint16_to_float64(uint16_t a, float_status *status)
{
return uint64_to_float64(a, status);
}
/* Float Min/Max */
/* min() and max() functions. These can't be implemented as
* 'compare and pick one input' because that would mishandle
* NaNs and +0 vs -0.
*
* minnum() and maxnum() functions. These are similar to the min()
* and max() functions but if one of the arguments is a QNaN and
* the other is numerical then the numerical argument is returned.
* SNaNs will get quietened before being returned.
* minnum() and maxnum correspond to the IEEE 754-2008 minNum()
* and maxNum() operations. min() and max() are the typical min/max
* semantics provided by many CPUs which predate that specification.
*
* minnummag() and maxnummag() functions correspond to minNumMag()
* and minNumMag() from the IEEE-754 2008.
*/
static FloatParts minmax_floats(FloatParts a, FloatParts b, bool ismin,
bool ieee, bool ismag, float_status *s)
{
if (unlikely(is_nan(a.cls) || is_nan(b.cls))) {
if (ieee) {
/* Takes two floating-point values `a' and `b', one of
* which is a NaN, and returns the appropriate NaN
* result. If either `a' or `b' is a signaling NaN,
* the invalid exception is raised.
*/
if (is_snan(a.cls) || is_snan(b.cls)) {
return pick_nan(a, b, s);
} else if (is_nan(a.cls) && !is_nan(b.cls)) {
return b;
} else if (is_nan(b.cls) && !is_nan(a.cls)) {
return a;
}
}
return pick_nan(a, b, s);
} else {
int a_exp, b_exp;
switch (a.cls) {
case float_class_normal:
a_exp = a.exp;
break;
case float_class_inf:
a_exp = INT_MAX;
break;
case float_class_zero:
a_exp = INT_MIN;
break;
default:
g_assert_not_reached();
break;
}
switch (b.cls) {
case float_class_normal:
b_exp = b.exp;
break;
case float_class_inf:
b_exp = INT_MAX;
break;
case float_class_zero:
b_exp = INT_MIN;
break;
default:
g_assert_not_reached();
break;
}
if (ismag && (a_exp != b_exp || a.frac != b.frac)) {
bool a_less = a_exp < b_exp;
if (a_exp == b_exp) {
a_less = a.frac < b.frac;
}
return a_less ^ ismin ? b : a;
}
if (a.sign == b.sign) {
bool a_less = a_exp < b_exp;
if (a_exp == b_exp) {
a_less = a.frac < b.frac;
}
return a.sign ^ a_less ^ ismin ? b : a;
} else {
return a.sign ^ ismin ? b : a;
}
}
}
#define MINMAX(sz, name, ismin, isiee, ismag) \
float ## sz float ## sz ## _ ## name(float ## sz a, float ## sz b, \
float_status *s) \
{ \
FloatParts pa = float ## sz ## _unpack_canonical(a, s); \
FloatParts pb = float ## sz ## _unpack_canonical(b, s); \
FloatParts pr = minmax_floats(pa, pb, ismin, isiee, ismag, s); \
\
return float ## sz ## _round_pack_canonical(pr, s); \
}
MINMAX(16, min, true, false, false)
MINMAX(16, minnum, true, true, false)
MINMAX(16, minnummag, true, true, true)
MINMAX(16, max, false, false, false)
MINMAX(16, maxnum, false, true, false)
MINMAX(16, maxnummag, false, true, true)
MINMAX(32, min, true, false, false)
MINMAX(32, minnum, true, true, false)
MINMAX(32, minnummag, true, true, true)
MINMAX(32, max, false, false, false)
MINMAX(32, maxnum, false, true, false)
MINMAX(32, maxnummag, false, true, true)
MINMAX(64, min, true, false, false)
MINMAX(64, minnum, true, true, false)
MINMAX(64, minnummag, true, true, true)
MINMAX(64, max, false, false, false)
MINMAX(64, maxnum, false, true, false)
MINMAX(64, maxnummag, false, true, true)
#undef MINMAX
/* Floating point compare */
static int compare_floats(FloatParts a, FloatParts b, bool is_quiet,
float_status *s)
{
if (is_nan(a.cls) || is_nan(b.cls)) {
if (!is_quiet ||
a.cls == float_class_snan ||
b.cls == float_class_snan) {
s->float_exception_flags |= float_flag_invalid;
}
return float_relation_unordered;
}
if (a.cls == float_class_zero) {
if (b.cls == float_class_zero) {
return float_relation_equal;
}
return b.sign ? float_relation_greater : float_relation_less;
} else if (b.cls == float_class_zero) {
return a.sign ? float_relation_less : float_relation_greater;
}
/* The only really important thing about infinity is its sign. If
* both are infinities the sign marks the smallest of the two.
*/
if (a.cls == float_class_inf) {
if ((b.cls == float_class_inf) && (a.sign == b.sign)) {
return float_relation_equal;
}
return a.sign ? float_relation_less : float_relation_greater;
} else if (b.cls == float_class_inf) {
return b.sign ? float_relation_greater : float_relation_less;
}
if (a.sign != b.sign) {
return a.sign ? float_relation_less : float_relation_greater;
}
if (a.exp == b.exp) {
if (a.frac == b.frac) {
return float_relation_equal;
}
if (a.sign) {
return a.frac > b.frac ?
float_relation_less : float_relation_greater;
} else {
return a.frac > b.frac ?
float_relation_greater : float_relation_less;
}
} else {
if (a.sign) {
return a.exp > b.exp ? float_relation_less : float_relation_greater;
} else {
return a.exp > b.exp ? float_relation_greater : float_relation_less;
}
}
}
#define COMPARE(sz) \
int float ## sz ## _compare(float ## sz a, float ## sz b, \
float_status *s) \
{ \
FloatParts pa = float ## sz ## _unpack_canonical(a, s); \
FloatParts pb = float ## sz ## _unpack_canonical(b, s); \
return compare_floats(pa, pb, false, s); \
} \
int float ## sz ## _compare_quiet(float ## sz a, float ## sz b, \
float_status *s) \
{ \
FloatParts pa = float ## sz ## _unpack_canonical(a, s); \
FloatParts pb = float ## sz ## _unpack_canonical(b, s); \
return compare_floats(pa, pb, true, s); \
}
COMPARE(16)
COMPARE(32)
COMPARE(64)
#undef COMPARE
/* Multiply A by 2 raised to the power N. */
static FloatParts scalbn_decomposed(FloatParts a, int n, float_status *s)
{
if (unlikely(is_nan(a.cls))) {
return return_nan(a, s);
}
if (a.cls == float_class_normal) {
/* The largest float type (even though not supported by FloatParts)
* is float128, which has a 15 bit exponent. Bounding N to 16 bits
* still allows rounding to infinity, without allowing overflow
* within the int32_t that backs FloatParts.exp.
*/
n = MIN(MAX(n, -0x10000), 0x10000);
a.exp += n;
}
return a;
}
float16 float16_scalbn(float16 a, int n, float_status *status)
{
FloatParts pa = float16_unpack_canonical(a, status);
FloatParts pr = scalbn_decomposed(pa, n, status);
return float16_round_pack_canonical(pr, status);
}
float32 float32_scalbn(float32 a, int n, float_status *status)
{
FloatParts pa = float32_unpack_canonical(a, status);
FloatParts pr = scalbn_decomposed(pa, n, status);
return float32_round_pack_canonical(pr, status);
}
float64 float64_scalbn(float64 a, int n, float_status *status)
{
FloatParts pa = float64_unpack_canonical(a, status);
FloatParts pr = scalbn_decomposed(pa, n, status);
return float64_round_pack_canonical(pr, status);
}
/*
* Square Root
*
* The old softfloat code did an approximation step before zeroing in
* on the final result. However for simpleness we just compute the
* square root by iterating down from the implicit bit to enough extra
* bits to ensure we get a correctly rounded result.
*
* This does mean however the calculation is slower than before,
* especially for 64 bit floats.
*/
static FloatParts sqrt_float(FloatParts a, float_status *s, const FloatFmt *p)
{
uint64_t a_frac, r_frac, s_frac;
int bit, last_bit;
if (is_nan(a.cls)) {
return return_nan(a, s);
}
if (a.cls == float_class_zero) {
return a; /* sqrt(+-0) = +-0 */
}
if (a.sign) {
s->float_exception_flags |= float_flag_invalid;
a.cls = float_class_dnan;
return a;
}
if (a.cls == float_class_inf) {
return a; /* sqrt(+inf) = +inf */
}
assert(a.cls == float_class_normal);
/* We need two overflow bits at the top. Adding room for that is a
* right shift. If the exponent is odd, we can discard the low bit
* by multiplying the fraction by 2; that's a left shift. Combine
* those and we shift right if the exponent is even.
*/
a_frac = a.frac;
if (!(a.exp & 1)) {
a_frac >>= 1;
}
a.exp >>= 1;
/* Bit-by-bit computation of sqrt. */
r_frac = 0;
s_frac = 0;
/* Iterate from implicit bit down to the 3 extra bits to compute a
* properly rounded result. Remember we've inserted one more bit
* at the top, so these positions are one less.
*/
bit = DECOMPOSED_BINARY_POINT - 1;
last_bit = MAX(p->frac_shift - 4, 0);
do {
uint64_t q = 1ULL << bit;
uint64_t t_frac = s_frac + q;
if (t_frac <= a_frac) {
s_frac = t_frac + q;
a_frac -= t_frac;
r_frac += q;
}
a_frac <<= 1;
} while (--bit >= last_bit);
/* Undo the right shift done above. If there is any remaining
* fraction, the result is inexact. Set the sticky bit.
*/
a.frac = (r_frac << 1) + (a_frac != 0);
return a;
}
float16 __attribute__((flatten)) float16_sqrt(float16 a, float_status *status)
{
FloatParts pa = float16_unpack_canonical(a, status);
FloatParts pr = sqrt_float(pa, status, &float16_params);
return float16_round_pack_canonical(pr, status);
}
float32 __attribute__((flatten)) float32_sqrt(float32 a, float_status *status)
{
FloatParts pa = float32_unpack_canonical(a, status);
FloatParts pr = sqrt_float(pa, status, &float32_params);
return float32_round_pack_canonical(pr, status);
}
float64 __attribute__((flatten)) float64_sqrt(float64 a, float_status *status)
{
FloatParts pa = float64_unpack_canonical(a, status);
FloatParts pr = sqrt_float(pa, status, &float64_params);
return float64_round_pack_canonical(pr, status);
}
/*----------------------------------------------------------------------------
| Takes a 64-bit fixed-point value `absZ' with binary point between bits 6
| and 7, and returns the properly rounded 32-bit integer corresponding to the
| input. If `zSign' is 1, the input is negated before being converted to an
| integer. Bit 63 of `absZ' must be zero. Ordinarily, the fixed-point input
| is simply rounded to an integer, with the inexact exception raised if the
| input cannot be represented exactly as an integer. However, if the fixed-
| point input is too large, the invalid exception is raised and the largest
| positive or negative integer is returned.
*----------------------------------------------------------------------------*/
static int32_t roundAndPackInt32(flag zSign, uint64_t absZ, float_status *status)
{
int8_t roundingMode;
flag roundNearestEven;
int8_t roundIncrement, roundBits;
int32_t z;
roundingMode = status->float_rounding_mode;
roundNearestEven = ( roundingMode == float_round_nearest_even );
switch (roundingMode) {
case float_round_nearest_even:
case float_round_ties_away:
roundIncrement = 0x40;
break;
case float_round_to_zero:
roundIncrement = 0;
break;
case float_round_up:
roundIncrement = zSign ? 0 : 0x7f;
break;
case float_round_down:
roundIncrement = zSign ? 0x7f : 0;
break;
default:
abort();
}
roundBits = absZ & 0x7F;
absZ = ( absZ + roundIncrement )>>7;
absZ &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven );
z = absZ;
if ( zSign ) z = - z;
if ( ( absZ>>32 ) || ( z && ( ( z < 0 ) ^ zSign ) ) ) {
float_raise(float_flag_invalid, status);
return zSign ? (int32_t) 0x80000000 : 0x7FFFFFFF;
}
if (roundBits) {
status->float_exception_flags |= float_flag_inexact;
}
return z;
}
/*----------------------------------------------------------------------------
| Takes the 128-bit fixed-point value formed by concatenating `absZ0' and
| `absZ1', with binary point between bits 63 and 64 (between the input words),
| and returns the properly rounded 64-bit integer corresponding to the input.
| If `zSign' is 1, the input is negated before being converted to an integer.
| Ordinarily, the fixed-point input is simply rounded to an integer, with
| the inexact exception raised if the input cannot be represented exactly as
| an integer. However, if the fixed-point input is too large, the invalid
| exception is raised and the largest positive or negative integer is
| returned.
*----------------------------------------------------------------------------*/
static int64_t roundAndPackInt64(flag zSign, uint64_t absZ0, uint64_t absZ1,
float_status *status)
{
int8_t roundingMode;
flag roundNearestEven, increment;
int64_t z;
roundingMode = status->float_rounding_mode;
roundNearestEven = ( roundingMode == float_round_nearest_even );
switch (roundingMode) {
case float_round_nearest_even:
case float_round_ties_away:
increment = ((int64_t) absZ1 < 0);
break;
case float_round_to_zero:
increment = 0;
break;
case float_round_up:
increment = !zSign && absZ1;
break;
case float_round_down:
increment = zSign && absZ1;
break;
default:
abort();
}
if ( increment ) {
++absZ0;
if ( absZ0 == 0 ) goto overflow;
absZ0 &= ~ ( ( (uint64_t) ( absZ1<<1 ) == 0 ) & roundNearestEven );
}
z = absZ0;
if ( zSign ) z = - z;
if ( z && ( ( z < 0 ) ^ zSign ) ) {
overflow:
float_raise(float_flag_invalid, status);
return
zSign ? (int64_t) LIT64( 0x8000000000000000 )
: LIT64( 0x7FFFFFFFFFFFFFFF );
}
if (absZ1) {
status->float_exception_flags |= float_flag_inexact;
}
return z;
}
/*----------------------------------------------------------------------------
| Takes the 128-bit fixed-point value formed by concatenating `absZ0' and
| `absZ1', with binary point between bits 63 and 64 (between the input words),
| and returns the properly rounded 64-bit unsigned integer corresponding to the
| input. Ordinarily, the fixed-point input is simply rounded to an integer,
| with the inexact exception raised if the input cannot be represented exactly
| as an integer. However, if the fixed-point input is too large, the invalid
| exception is raised and the largest unsigned integer is returned.
*----------------------------------------------------------------------------*/
static int64_t roundAndPackUint64(flag zSign, uint64_t absZ0,
uint64_t absZ1, float_status *status)
{
int8_t roundingMode;
flag roundNearestEven, increment;
roundingMode = status->float_rounding_mode;
roundNearestEven = (roundingMode == float_round_nearest_even);
switch (roundingMode) {
case float_round_nearest_even:
case float_round_ties_away:
increment = ((int64_t)absZ1 < 0);
break;
case float_round_to_zero:
increment = 0;
break;
case float_round_up:
increment = !zSign && absZ1;
break;
case float_round_down:
increment = zSign && absZ1;
break;
default:
abort();
}
if (increment) {
++absZ0;
if (absZ0 == 0) {
float_raise(float_flag_invalid, status);
return LIT64(0xFFFFFFFFFFFFFFFF);
}
absZ0 &= ~(((uint64_t)(absZ1<<1) == 0) & roundNearestEven);
}
if (zSign && absZ0) {
float_raise(float_flag_invalid, status);
return 0;
}
if (absZ1) {
status->float_exception_flags |= float_flag_inexact;
}
return absZ0;
}
/*----------------------------------------------------------------------------
| If `a' is denormal and we are in flush-to-zero mode then set the
| input-denormal exception and return zero. Otherwise just return the value.
*----------------------------------------------------------------------------*/
float32 float32_squash_input_denormal(float32 a, float_status *status)
{
if (status->flush_inputs_to_zero) {
if (extractFloat32Exp(a) == 0 && extractFloat32Frac(a) != 0) {
float_raise(float_flag_input_denormal, status);
return make_float32(float32_val(a) & 0x80000000);
}
}
return a;
}
/*----------------------------------------------------------------------------
| Normalizes the subnormal single-precision floating-point value represented
| by the denormalized significand `aSig'. The normalized exponent and
| significand are stored at the locations pointed to by `zExpPtr' and
| `zSigPtr', respectively.
*----------------------------------------------------------------------------*/
static void
normalizeFloat32Subnormal(uint32_t aSig, int *zExpPtr, uint32_t *zSigPtr)
{
int8_t shiftCount;
shiftCount = countLeadingZeros32( aSig ) - 8;
*zSigPtr = aSig<<shiftCount;
*zExpPtr = 1 - shiftCount;
}
/*----------------------------------------------------------------------------
| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
| and significand `zSig', and returns the proper single-precision floating-
| point value corresponding to the abstract input. Ordinarily, the abstract
| value is simply rounded and packed into the single-precision format, with
| the inexact exception raised if the abstract input cannot be represented
| exactly. However, if the abstract value is too large, the overflow and
| inexact exceptions are raised and an infinity or maximal finite value is
| returned. If the abstract value is too small, the input value is rounded to
| a subnormal number, and the underflow and inexact exceptions are raised if
| the abstract input cannot be represented exactly as a subnormal single-
| precision floating-point number.
| The input significand `zSig' has its binary point between bits 30
| and 29, which is 7 bits to the left of the usual location. This shifted
| significand must be normalized or smaller. If `zSig' is not normalized,
| `zExp' must be 0; in that case, the result returned is a subnormal number,
| and it must not require rounding. In the usual case that `zSig' is
| normalized, `zExp' must be 1 less than the ``true'' floating-point exponent.
| The handling of underflow and overflow follows the IEC/IEEE Standard for
| Binary Floating-Point Arithmetic.
*----------------------------------------------------------------------------*/
static float32 roundAndPackFloat32(flag zSign, int zExp, uint32_t zSig,
float_status *status)
{
int8_t roundingMode;
flag roundNearestEven;
int8_t roundIncrement, roundBits;
flag isTiny;
roundingMode = status->float_rounding_mode;
roundNearestEven = ( roundingMode == float_round_nearest_even );
switch (roundingMode) {
case float_round_nearest_even:
case float_round_ties_away:
roundIncrement = 0x40;
break;
case float_round_to_zero:
roundIncrement = 0;
break;
case float_round_up:
roundIncrement = zSign ? 0 : 0x7f;
break;
case float_round_down:
roundIncrement = zSign ? 0x7f : 0;
break;
default:
abort();
break;
}
roundBits = zSig & 0x7F;
if ( 0xFD <= (uint16_t) zExp ) {
if ( ( 0xFD < zExp )
|| ( ( zExp == 0xFD )
&& ( (int32_t) ( zSig + roundIncrement ) < 0 ) )
) {
float_raise(float_flag_overflow | float_flag_inexact, status);
return packFloat32( zSign, 0xFF, - ( roundIncrement == 0 ));
}
if ( zExp < 0 ) {
if (status->flush_to_zero) {
float_raise(float_flag_output_denormal, status);
return packFloat32(zSign, 0, 0);
}
isTiny =
(status->float_detect_tininess
== float_tininess_before_rounding)
|| ( zExp < -1 )
|| ( zSig + roundIncrement < 0x80000000 );
shift32RightJamming( zSig, - zExp, &zSig );
zExp = 0;
roundBits = zSig & 0x7F;
if (isTiny && roundBits) {
float_raise(float_flag_underflow, status);
}
}
}
if (roundBits) {
status->float_exception_flags |= float_flag_inexact;
}
zSig = ( zSig + roundIncrement )>>7;
zSig &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven );
if ( zSig == 0 ) zExp = 0;
return packFloat32( zSign, zExp, zSig );
}
/*----------------------------------------------------------------------------
| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
| and significand `zSig', and returns the proper single-precision floating-
| point value corresponding to the abstract input. This routine is just like
| `roundAndPackFloat32' except that `zSig' does not have to be normalized.
| Bit 31 of `zSig' must be zero, and `zExp' must be 1 less than the ``true''
| floating-point exponent.
*----------------------------------------------------------------------------*/
static float32
normalizeRoundAndPackFloat32(flag zSign, int zExp, uint32_t zSig,
float_status *status)
{
int8_t shiftCount;
shiftCount = countLeadingZeros32( zSig ) - 1;
return roundAndPackFloat32(zSign, zExp - shiftCount, zSig<<shiftCount,
status);
}
/*----------------------------------------------------------------------------
| If `a' is denormal and we are in flush-to-zero mode then set the
| input-denormal exception and return zero. Otherwise just return the value.
*----------------------------------------------------------------------------*/
float64 float64_squash_input_denormal(float64 a, float_status *status)
{
if (status->flush_inputs_to_zero) {
if (extractFloat64Exp(a) == 0 && extractFloat64Frac(a) != 0) {
float_raise(float_flag_input_denormal, status);
return make_float64(float64_val(a) & (1ULL << 63));
}
}
return a;
}
/*----------------------------------------------------------------------------
| Normalizes the subnormal double-precision floating-point value represented
| by the denormalized significand `aSig'. The normalized exponent and
| significand are stored at the locations pointed to by `zExpPtr' and
| `zSigPtr', respectively.
*----------------------------------------------------------------------------*/
static void
normalizeFloat64Subnormal(uint64_t aSig, int *zExpPtr, uint64_t *zSigPtr)
{
int8_t shiftCount;
shiftCount = countLeadingZeros64( aSig ) - 11;
*zSigPtr = aSig<<shiftCount;
*zExpPtr = 1 - shiftCount;
}
/*----------------------------------------------------------------------------
| Packs the sign `zSign', exponent `zExp', and significand `zSig' into a
| double-precision floating-point value, returning the result. After being
| shifted into the proper positions, the three fields are simply added
| together to form the result. This means that any integer portion of `zSig'
| will be added into the exponent. Since a properly normalized significand
| will have an integer portion equal to 1, the `zExp' input should be 1 less
| than the desired result exponent whenever `zSig' is a complete, normalized
| significand.
*----------------------------------------------------------------------------*/
static inline float64 packFloat64(flag zSign, int zExp, uint64_t zSig)
{
return make_float64(
( ( (uint64_t) zSign )<<63 ) + ( ( (uint64_t) zExp )<<52 ) + zSig);
}
/*----------------------------------------------------------------------------
| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
| and significand `zSig', and returns the proper double-precision floating-
| point value corresponding to the abstract input. Ordinarily, the abstract
| value is simply rounded and packed into the double-precision format, with
| the inexact exception raised if the abstract input cannot be represented
| exactly. However, if the abstract value is too large, the overflow and
| inexact exceptions are raised and an infinity or maximal finite value is
| returned. If the abstract value is too small, the input value is rounded to
| a subnormal number, and the underflow and inexact exceptions are raised if
| the abstract input cannot be represented exactly as a subnormal double-
| precision floating-point number.
| The input significand `zSig' has its binary point between bits 62
| and 61, which is 10 bits to the left of the usual location. This shifted
| significand must be normalized or smaller. If `zSig' is not normalized,
| `zExp' must be 0; in that case, the result returned is a subnormal number,
| and it must not require rounding. In the usual case that `zSig' is
| normalized, `zExp' must be 1 less than the ``true'' floating-point exponent.
| The handling of underflow and overflow follows the IEC/IEEE Standard for
| Binary Floating-Point Arithmetic.
*----------------------------------------------------------------------------*/
static float64 roundAndPackFloat64(flag zSign, int zExp, uint64_t zSig,
float_status *status)
{
int8_t roundingMode;
flag roundNearestEven;
int roundIncrement, roundBits;
flag isTiny;
roundingMode = status->float_rounding_mode;
roundNearestEven = ( roundingMode == float_round_nearest_even );
switch (roundingMode) {
case float_round_nearest_even:
case float_round_ties_away:
roundIncrement = 0x200;
break;
case float_round_to_zero:
roundIncrement = 0;
break;
case float_round_up:
roundIncrement = zSign ? 0 : 0x3ff;
break;
case float_round_down:
roundIncrement = zSign ? 0x3ff : 0;
break;
case float_round_to_odd:
roundIncrement = (zSig & 0x400) ? 0 : 0x3ff;
break;
default:
abort();
}
roundBits = zSig & 0x3FF;
if ( 0x7FD <= (uint16_t) zExp ) {
if ( ( 0x7FD < zExp )
|| ( ( zExp == 0x7FD )
&& ( (int64_t) ( zSig + roundIncrement ) < 0 ) )
) {
bool overflow_to_inf = roundingMode != float_round_to_odd &&
roundIncrement != 0;
float_raise(float_flag_overflow | float_flag_inexact, status);
return packFloat64(zSign, 0x7FF, -(!overflow_to_inf));
}
if ( zExp < 0 ) {
if (status->flush_to_zero) {
float_raise(float_flag_output_denormal, status);
return packFloat64(zSign, 0, 0);
}
isTiny =
(status->float_detect_tininess
== float_tininess_before_rounding)
|| ( zExp < -1 )
|| ( zSig + roundIncrement < LIT64( 0x8000000000000000 ) );
shift64RightJamming( zSig, - zExp, &zSig );
zExp = 0;
roundBits = zSig & 0x3FF;
if (isTiny && roundBits) {
float_raise(float_flag_underflow, status);
}
if (roundingMode == float_round_to_odd) {
/*
* For round-to-odd case, the roundIncrement depends on
* zSig which just changed.
*/
roundIncrement = (zSig & 0x400) ? 0 : 0x3ff;
}
}
}
if (roundBits) {
status->float_exception_flags |= float_flag_inexact;
}
zSig = ( zSig + roundIncrement )>>10;
zSig &= ~ ( ( ( roundBits ^ 0x200 ) == 0 ) & roundNearestEven );
if ( zSig == 0 ) zExp = 0;
return packFloat64( zSign, zExp, zSig );
}
/*----------------------------------------------------------------------------
| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
| and significand `zSig', and returns the proper double-precision floating-
| point value corresponding to the abstract input. This routine is just like
| `roundAndPackFloat64' except that `zSig' does not have to be normalized.
| Bit 63 of `zSig' must be zero, and `zExp' must be 1 less than the ``true''
| floating-point exponent.
*----------------------------------------------------------------------------*/
static float64
normalizeRoundAndPackFloat64(flag zSign, int zExp, uint64_t zSig,
float_status *status)
{
int8_t shiftCount;
shiftCount = countLeadingZeros64( zSig ) - 1;
return roundAndPackFloat64(zSign, zExp - shiftCount, zSig<<shiftCount,
status);
}
/*----------------------------------------------------------------------------
| Normalizes the subnormal extended double-precision floating-point value
| represented by the denormalized significand `aSig'. The normalized exponent
| and significand are stored at the locations pointed to by `zExpPtr' and
| `zSigPtr', respectively.
*----------------------------------------------------------------------------*/
void normalizeFloatx80Subnormal(uint64_t aSig, int32_t *zExpPtr,
uint64_t *zSigPtr)
{
int8_t shiftCount;
shiftCount = countLeadingZeros64( aSig );
*zSigPtr = aSig<<shiftCount;
*zExpPtr = 1 - shiftCount;
}
/*----------------------------------------------------------------------------
| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
| and extended significand formed by the concatenation of `zSig0' and `zSig1',
| and returns the proper extended double-precision floating-point value
| corresponding to the abstract input. Ordinarily, the abstract value is
| rounded and packed into the extended double-precision format, with the
| inexact exception raised if the abstract input cannot be represented
| exactly. However, if the abstract value is too large, the overflow and
| inexact exceptions are raised and an infinity or maximal finite value is
| returned. If the abstract value is too small, the input value is rounded to
| a subnormal number, and the underflow and inexact exceptions are raised if
| the abstract input cannot be represented exactly as a subnormal extended
| double-precision floating-point number.
| If `roundingPrecision' is 32 or 64, the result is rounded to the same
| number of bits as single or double precision, respectively. Otherwise, the
| result is rounded to the full precision of the extended double-precision
| format.
| The input significand must be normalized or smaller. If the input
| significand is not normalized, `zExp' must be 0; in that case, the result
| returned is a subnormal number, and it must not require rounding. The
| handling of underflow and overflow follows the IEC/IEEE Standard for Binary
| Floating-Point Arithmetic.
*----------------------------------------------------------------------------*/
floatx80 roundAndPackFloatx80(int8_t roundingPrecision, flag zSign,
int32_t zExp, uint64_t zSig0, uint64_t zSig1,
float_status *status)
{
int8_t roundingMode;
flag roundNearestEven, increment, isTiny;
int64_t roundIncrement, roundMask, roundBits;
roundingMode = status->float_rounding_mode;
roundNearestEven = ( roundingMode == float_round_nearest_even );
if ( roundingPrecision == 80 ) goto precision80;
if ( roundingPrecision == 64 ) {
roundIncrement = LIT64( 0x0000000000000400 );
roundMask = LIT64( 0x00000000000007FF );
}
else if ( roundingPrecision == 32 ) {
roundIncrement = LIT64( 0x0000008000000000 );
roundMask = LIT64( 0x000000FFFFFFFFFF );
}
else {
goto precision80;
}
zSig0 |= ( zSig1 != 0 );
switch (roundingMode) {
case float_round_nearest_even:
case float_round_ties_away:
break;
case float_round_to_zero:
roundIncrement = 0;
break;
case float_round_up:
roundIncrement = zSign ? 0 : roundMask;
break;
case float_round_down:
roundIncrement = zSign ? roundMask : 0;
break;
default:
abort();
}
roundBits = zSig0 & roundMask;
if ( 0x7FFD <= (uint32_t) ( zExp - 1 ) ) {
if ( ( 0x7FFE < zExp )
|| ( ( zExp == 0x7FFE ) && ( zSig0 + roundIncrement < zSig0 ) )
) {
goto overflow;
}
if ( zExp <= 0 ) {
if (status->flush_to_zero) {
float_raise(float_flag_output_denormal, status);
return packFloatx80(zSign, 0, 0);
}
isTiny =
(status->float_detect_tininess
== float_tininess_before_rounding)
|| ( zExp < 0 )
|| ( zSig0 <= zSig0 + roundIncrement );
shift64RightJamming( zSig0, 1 - zExp, &zSig0 );
zExp = 0;
roundBits = zSig0 & roundMask;
if (isTiny && roundBits) {
float_raise(float_flag_underflow, status);
}
if (roundBits) {
status->float_exception_flags |= float_flag_inexact;
}
zSig0 += roundIncrement;
if ( (int64_t) zSig0 < 0 ) zExp = 1;
roundIncrement = roundMask + 1;
if ( roundNearestEven && ( roundBits<<1 == roundIncrement ) ) {
roundMask |= roundIncrement;
}
zSig0 &= ~ roundMask;
return packFloatx80( zSign, zExp, zSig0 );
}
}
if (roundBits) {
status->float_exception_flags |= float_flag_inexact;
}
zSig0 += roundIncrement;
if ( zSig0 < roundIncrement ) {
++zExp;
zSig0 = LIT64( 0x8000000000000000 );
}
roundIncrement = roundMask + 1;
if ( roundNearestEven && ( roundBits<<1 == roundIncrement ) ) {
roundMask |= roundIncrement;
}
zSig0 &= ~ roundMask;
if ( zSig0 == 0 ) zExp = 0;
return packFloatx80( zSign, zExp, zSig0 );
precision80:
switch (roundingMode) {
case float_round_nearest_even:
case float_round_ties_away:
increment = ((int64_t)zSig1 < 0);
break;
case float_round_to_zero:
increment = 0;
break;
case float_round_up:
increment = !zSign && zSig1;
break;
case float_round_down:
increment = zSign && zSig1;
break;
default:
abort();
}
if ( 0x7FFD <= (uint32_t) ( zExp - 1 ) ) {
if ( ( 0x7FFE < zExp )
|| ( ( zExp == 0x7FFE )
&& ( zSig0 == LIT64( 0xFFFFFFFFFFFFFFFF ) )
&& increment
)
) {
roundMask = 0;
overflow:
float_raise(float_flag_overflow | float_flag_inexact, status);
if ( ( roundingMode == float_round_to_zero )
|| ( zSign && ( roundingMode == float_round_up ) )
|| ( ! zSign && ( roundingMode == float_round_down ) )
) {
return packFloatx80( zSign, 0x7FFE, ~ roundMask );
}
return packFloatx80(zSign,
floatx80_infinity_high,
floatx80_infinity_low);
}
if ( zExp <= 0 ) {
isTiny =
(status->float_detect_tininess
== float_tininess_before_rounding)
|| ( zExp < 0 )
|| ! increment
|| ( zSig0 < LIT64( 0xFFFFFFFFFFFFFFFF ) );
shift64ExtraRightJamming( zSig0, zSig1, 1 - zExp, &zSig0, &zSig1 );
zExp = 0;
if (isTiny && zSig1) {
float_raise(float_flag_underflow, status);
}
if (zSig1) {
status->float_exception_flags |= float_flag_inexact;
}
switch (roundingMode) {
case float_round_nearest_even:
case float_round_ties_away:
increment = ((int64_t)zSig1 < 0);
break;
case float_round_to_zero:
increment = 0;
break;
case float_round_up:
increment = !zSign && zSig1;
break;