blob: 975db2c7ba0064553bdb408a9771c15e5385d220 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0
// ----------------------------------------------------------------------------
// Copyright 2011-2020 Arm Limited
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// ----------------------------------------------------------------------------
/**
* @brief Soft-float library for IEEE-754.
*/
#include "astc_mathlib.h"
/******************************************
helper functions and their lookup tables
******************************************/
/* count leading zeros functions. Only used when the input is nonzero. */
#if defined(__GNUC__) && (defined(__i386) || defined(__amd64))
#elif defined(__arm__) && defined(__ARMCC_VERSION)
#elif defined(__arm__) && defined(__GNUC__)
#else
/* table used for the slow default versions. */
static const uint8_t clz_table[256] =
{
8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
#endif
/*
32-bit count-leading-zeros function: use the Assembly instruction whenever possible. */
uint32_t clz32(uint32_t inp)
{
#if defined(__GNUC__) && (defined(__i386) || defined(__amd64))
uint32_t bsr;
__asm__("bsrl %1, %0": "=r"(bsr):"r"(inp | 1));
return 31 - bsr;
#else
#if defined(__arm__) && defined(__ARMCC_VERSION)
return __clz(inp); /* armcc builtin */
#else
#if defined(__arm__) && defined(__GNUC__)
uint32_t lz;
__asm__("clz %0, %1": "=r"(lz):"r"(inp));
return lz;
#else
/* slow default version */
uint32_t summa = 24;
if (inp >= UINT32_C(0x10000))
{
inp >>= 16;
summa -= 16;
}
if (inp >= UINT32_C(0x100))
{
inp >>= 8;
summa -= 8;
}
return summa + clz_table[inp];
#endif
#endif
#endif
}
/* convert from FP16 to FP32. */
sf32 sf16_to_sf32(sf16 inp)
{
uint32_t inpx = inp;
/*
This table contains, for every FP16 sign/exponent value combination,
the difference between the input FP16 value and the value obtained
by shifting the correct FP32 result right by 13 bits.
This table allows us to handle every case except denormals and NaN
with just 1 table lookup, 2 shifts and 1 add.
*/
#define WITH_MB(a) INT32_C((a) | (1 << 31))
static const int32_t tbl[64] =
{
WITH_MB(0x00000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000),
INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000),
INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000),
INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), INT32_C(0x1C000), WITH_MB(0x38000),
WITH_MB(0x38000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000),
INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000),
INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000),
INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), INT32_C(0x54000), WITH_MB(0x70000)
};
int32_t res = tbl[inpx >> 10];
res += inpx;
/* the normal cases: the MSB of 'res' is not set. */
if (res >= 0) /* signed compare */
return res << 13;
/* Infinity and Zero: the bottom 10 bits of 'res' are clear. */
if ((res & UINT32_C(0x3FF)) == 0)
return res << 13;
/* NaN: the exponent field of 'inp' is not zero; NaNs must be quietened. */
if ((inpx & 0x7C00) != 0)
return (res << 13) | UINT32_C(0x400000);
/* the remaining cases are Denormals. */
{
uint32_t sign = (inpx & UINT32_C(0x8000)) << 16;
uint32_t mskval = inpx & UINT32_C(0x7FFF);
uint32_t leadingzeroes = clz32(mskval);
mskval <<= leadingzeroes;
return (mskval >> 8) + ((0x85 - leadingzeroes) << 23) + sign;
}
}
/* convert from soft-float to native-float */
float sf16_to_float(sf16 p)
{
if32 i;
i.u = sf16_to_sf32(p);
return i.f;
}