Home | History | Annotate | Line # | Download | only in compiler
      1 /*
      2  * Copyright (C) 2021 Alyssa Rosenzweig <alyssa (at) rosenzweig.io>
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8  * and/or sell copies of the Software, and to permit persons to whom the
      9  * Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice (including the next
     12  * paragraph) shall be included in all copies or substantial portions of the
     13  * Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     21  * SOFTWARE.
     22  */
     23 
     24 #ifndef __AGX_MINIFLOAT_H_
     25 #define __AGX_MINIFLOAT_H_
     26 
     27 #include <math.h>
     28 #include "util/macros.h"
     29 
     30 /* AGX includes an 8-bit floating-point format for small dyadic immediates,
     31  * consisting of 3 bits for the exponent, 4 bits for the mantissa, and 1-bit
     32  * for sign, in the usual order. Zero exponent has special handling. */
     33 
     34 static inline float
     35 agx_minifloat_decode(uint8_t imm)
     36 {
     37    float sign = (imm & 0x80) ? -1.0 : 1.0;
     38    signed exp = (imm & 0x70) >> 4;
     39    unsigned mantissa = (imm & 0xF);
     40 
     41    if (exp)
     42       return ldexpf(sign * (float) (mantissa | 0x10), exp - 7);
     43    else
     44       return ldexpf(sign * ((float) mantissa), -6);
     45 }
     46 
     47 /* Encodes a float. Results are only valid if the float can be represented
     48  * exactly, if not the result of this function is UNDEFINED. signbit() is used
     49  * to ensure -0.0 is handled correctly. */
     50 
     51 static inline uint8_t
     52 agx_minifloat_encode(float f)
     53 {
     54    unsigned sign = signbit(f) ? 0x80 : 0;
     55    f = fabsf(f);
     56 
     57    /* frac is in [0.5, 1) and f = frac * 2^exp */
     58    int exp = 0;
     59    float frac = frexpf(f, &exp);
     60 
     61    if (f >= 0.25) {
     62       unsigned mantissa = (frac * 32.0);
     63       exp -= 5; /* 2^5 = 32 */
     64       exp = CLAMP(exp + 7, 0, 7);
     65 
     66       assert(mantissa >= 0x10 && mantissa < 0x20);
     67       assert(exp >= 1);
     68 
     69       return sign | (exp << 4) | (mantissa & 0xF);
     70    } else {
     71       unsigned mantissa = (f * 64.0f);
     72       assert(mantissa < 0x10);
     73 
     74       return sign | mantissa;
     75    }
     76 }
     77 
     78 static inline bool
     79 agx_minifloat_exact(float f)
     80 {
     81    float f_ = agx_minifloat_decode(agx_minifloat_encode(f));
     82    return memcmp(&f, &f_, sizeof(float)) == 0;
     83 }
     84 
     85 #ifndef NDEBUG
     86 static inline void
     87 agx_minifloat_tests(void)
     88 {
     89    /* Decode some representative values */
     90    assert(agx_minifloat_decode(0) == 0.0f);
     91    assert(agx_minifloat_decode(25) == 0.390625f);
     92    assert(agx_minifloat_decode(135) == -0.109375f);
     93    assert(agx_minifloat_decode(255) == -31.0);
     94 
     95    /* Verify exactness */
     96    assert(agx_minifloat_exact(0.0f));
     97    assert(agx_minifloat_exact(0.390625f));
     98    assert(agx_minifloat_exact(-0.109375f));
     99    assert(agx_minifloat_exact(-31.0));
    100    assert(!agx_minifloat_exact(3.141f));
    101    assert(!agx_minifloat_exact(2.718f));
    102    assert(!agx_minifloat_exact(1.618f));
    103 
    104    /* Check that all values round trip */
    105    for (unsigned i = 0; i < 0x100; ++i) {
    106       float f = agx_minifloat_decode(i);
    107       assert(agx_minifloat_encode(f) == i);
    108       assert(agx_minifloat_exact(f));
    109    }
    110 }
    111 #endif
    112 
    113 #endif
    114