Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <stdlib.h>
     18 #include <stdio.h>
     19 #include <math.h>
     20 
     21 // This utility generates a Java file containing data used in the CTS test for
     22 // FP16 arithmetic.  The Java file containes a class with the following fields:
     23 //     * A 1D array of length 'n' containing various constants used in the test
     24 //     * Four n x n x 2 arrays, each containing the reference output for
     25 //     pair-wise addition, subtraction, multiplication and division.  The
     26 //     reference output is a range accounting for tolerable error.  The
     27 //     acceptable error is 3 x ULP for division and 1 x ULP for other
     28 //     operations.
     29 
     30 typedef __fp16 half;
     31 
     32 // Macros for names of the package, class and fields in the generated java file
     33 #define PACKAGE_NAME "android.renderscript.cts"
     34 #define CLASS_NAME "Float16TestData"
     35 #define INPUT_ARRAY "input"
     36 #define OUTPUT_ARRAY_ADD "ReferenceOutputForAdd"
     37 #define OUTPUT_ARRAY_SUB "ReferenceOutputForSub"
     38 #define OUTPUT_ARRAY_MUL "ReferenceOutputForMul"
     39 #define OUTPUT_ARRAY_DIV "ReferenceOutputForDiv"
     40 
     41 // Structure to hold an FP16 constant and its human-readable description, to be
     42 // added as a comment, in the generated Java file
     43 typedef struct {
     44   unsigned short value;
     45   const char* description;
     46 } FP16Constant;
     47 
     48 FP16Constant input[] = {
     49     { 0b0011110000000000, "one" },
     50     { 0b0100000000000000, "two" },
     51     { 0b0000000000000001, "smallest subnormal" },
     52     { 0b0000001111111111, "largest subnormal" },
     53     { 0b0000010000000000, "smallest normal" },
     54     { 0b0111101111111111, "largest normal" },
     55     { 0x3880, "0.562500" },
     56     { 0x3e80, "1.625000" },
     57     { 0x5140, "42.000000" },
     58     { 0x5ac0, "216.000000" },
     59     { 0x6c75, "4564.000000" },
     60     { 0x7b53, "60000.000000" },
     61     { 0b1011110000000000, "negative one" },
     62     { 0b1100000000000000, "negative two" },
     63     { 0b1000000000000001, "negative (smallest subnormal)" },
     64     { 0b1000001111111111, "negative (largest subnormal)" },
     65     { 0b1000010000000000, "negative (smallest normal)" },
     66     { 0b1111101111111111, "negative (largest normal)" },
     67     { 0xb880, "-0.562500" },
     68     { 0xbe80, "-1.625000" },
     69     { 0xd140, "-42.000000" },
     70     { 0xdac0, "-216.000000" },
     71     { 0xec75, "-4564.000000" },
     72     { 0xfb53, "-60000.000000" },
     73     { 0b0000000000000000, "zero" },
     74     { 0b0111110000000000, "infinity" },
     75     { 0b1000000000000000, "negative zero" },
     76     { 0b1111110000000000, "negative infinity" },
     77     { 0b0111110000000001, "nan" },
     78 };
     79 
     80 const int numInputs = sizeof(input) / sizeof(FP16Constant);
     81 
     82 // 16-bit masks for extracting sign, exponent and mantissa bits
     83 static unsigned short SIGN_MASK     = 0x8000;
     84 static unsigned short EXPONENT_MASK = 0x7C00;
     85 static unsigned short MANTISSA_MASK = 0x03FF;
     86 
     87 // NaN has all exponent bits set to 1 and a non-zero mantissa
     88 int isFloat16NaN(unsigned short val) {
     89   return (val & EXPONENT_MASK) == EXPONENT_MASK &&
     90          (val & MANTISSA_MASK) != 0;
     91 }
     92 
     93 // Infinity has all exponent bits set to 1 and zeroes in mantissa
     94 int isFloat16Infinite(unsigned short val) {
     95   return (val & EXPONENT_MASK) == EXPONENT_MASK &&
     96          (val & MANTISSA_MASK) == 0;
     97 }
     98 
     99 // Subnormal numbers have exponent bits set to 0 and a non-zero mantissa
    100 int isFloat16SubNormal(unsigned short val) {
    101     return (val & EXPONENT_MASK) == 0 && (val & MANTISSA_MASK) != 0;
    102 }
    103 
    104 // Negativity test checks the sign bit
    105 int isFloat16Negative(unsigned short val) {
    106     return (val & SIGN_MASK) != 0;
    107 }
    108 
    109 // Interpret a short as a FP16 value and convert to float
    110 float half2float(unsigned short s) {
    111   half h = *(half *) &s;
    112   return (float) h;
    113 }
    114 
    115 // Return the short value representing a float value in FP16
    116 unsigned short float2half(float f) {
    117   half h = (half) f;
    118   return *(unsigned short *) &h;
    119 }
    120 
    121 // Compute ULP for 'value' and store value +/- tolerance * ULP in bounds sarray
    122 void getErrorBar(unsigned short value, int tolerance, unsigned short bounds[2]) {
    123   // Validate 'tolerance' parameter
    124   if (tolerance != 1 && tolerance != 3) {
    125     fprintf(stderr, "Allowed ULP error should either be 1 or 3, and not %d\n",
    126             tolerance);
    127     exit(0);
    128   }
    129 
    130   half hValue = *(half *) &value;
    131   half ulp;
    132 
    133   // For Infinity and NaN, bounds are equal to 'value'
    134   if (isFloat16Infinite(value) || isFloat16NaN(value)) {
    135     bounds[0] = value;
    136     bounds[1] = value;
    137     return;
    138   }
    139 
    140   // Compute ULP
    141   if (isFloat16SubNormal(value)) {
    142     // 1 ulp for a subnormal number is the smallest possible subnormal
    143     unsigned short ulpInShort = 0b0000000000000001;
    144     ulp = *(half *) &ulpInShort;
    145   }
    146   else {
    147     // 1 ulp for a non-subnormal number is (b - a) where
    148     //   - a has same exponent as 'value', zeroes for sign and mantissa
    149     //   - b has same exponent and sign as 'a', and has '1' in the mantissa
    150     // (b - a) gives the ULP by getting rid of the implied '1' at the front of
    151     // the mantissa
    152     unsigned short a = (value & EXPONENT_MASK);
    153     unsigned short b = (a | 1);
    154     half hA = *(half *) &a;
    155     half hB = *(half *) &b;
    156     ulp = hB - hA;
    157   }
    158 
    159   // Compute error bar based on error tolerance
    160   half lb = hValue - tolerance * ulp;
    161   half ub = hValue + tolerance * ulp;
    162   if (lb > ub) {
    163     fprintf(stderr, "Warning! inconsistency in bounds\n");
    164     fprintf(stderr, "Value: %f, ulp: %f\n", (float) hValue, (float) ulp);
    165     fprintf(stderr, "lb: %f ub: %f\n", (float) lb, (float) ub);
    166     fprintf(stderr, "lb: %x ub: %x\n", *(unsigned short *) &lb, *(unsigned short *) &ub);
    167   }
    168 
    169   // Set the bounds
    170   bounds[0] = *(unsigned short *) &lb;
    171   bounds[1] = *(unsigned short *) &ub;
    172 
    173   // RS allows flush-to-zero for sub-normal results in relaxed precision.
    174   // Flush lower bound of a positive sub-normal result to zero.
    175   if (!isFloat16Negative(bounds[0]) && isFloat16SubNormal(bounds[0]))
    176     bounds[0] = 0x0;
    177   // Flush upper bound of a negative sub-normal result to negative zero.
    178   if (isFloat16Negative(bounds[1]) && isFloat16SubNormal(bounds[1]))
    179     bounds[1] = 0x0 | SIGN_MASK;
    180 
    181 }
    182 
    183 // Utilities that take 'unsigned short' representations of two fp16 values and
    184 // return the result of an arithmetic operation as an 'unsigned short'.
    185 typedef unsigned short operation_t(unsigned short, unsigned short);
    186 
    187 unsigned short add(unsigned short a, unsigned short b) {
    188   float op1 = half2float(a);
    189   float op2 = half2float(b);
    190   return float2half(op1 + op2);
    191 }
    192 
    193 unsigned short subtract(unsigned short a, unsigned short b) {
    194   float op1 = half2float(a);
    195   float op2 = half2float(b);
    196   return float2half(op1 - op2);
    197 }
    198 
    199 unsigned short multiply(unsigned short a, unsigned short b) {
    200   float op1 = half2float(a);
    201   float op2 = half2float(b);
    202   return float2half(op1 * op2);
    203 }
    204 
    205 unsigned short divide(unsigned short a, unsigned short b) {
    206   float op1 = half2float(a);
    207   float op2 = half2float(b);
    208   return float2half(op1 / op2);
    209 }
    210 
    211 // Print Java code that initializes the input array (along with the description
    212 // of the constant as a comment)
    213 void printInput() {
    214   printf("static short[] %s = {\n", INPUT_ARRAY);
    215 
    216   for (int x = 0; x < numInputs; x ++)
    217     printf("(short) 0x%04x, // %s\n", input[x].value, input[x].description);
    218 
    219   printf("};\n\n");
    220 }
    221 
    222 // Print Java code that initializes the output array with the acceptable bounds
    223 // on the output.  For each pair of inputs, bounds are calculated on the result
    224 // from applying 'operation' on the pair.
    225 void printReferenceOutput(const char *fieldName, operation_t operation,
    226                           int tolerance) {
    227   unsigned short result;
    228   unsigned short resultBounds[2];
    229 
    230   printf("static short[][][] %s = {\n", fieldName);
    231 
    232   for (int x = 0; x < numInputs; x ++) {
    233     printf("{");
    234     for (int y = 0; y < numInputs; y ++) {
    235       // Apply 'operation' and compute error bounds for the result.
    236       result = operation(input[x].value, input[y].value);
    237       getErrorBar(result, tolerance, resultBounds);
    238 
    239       printf("{ (short) 0x%04x, (short) 0x%04x},", resultBounds[0],
    240                                                    resultBounds[1]);
    241     }
    242     printf("},\n");
    243   }
    244 
    245   printf("};\n\n");
    246 }
    247 
    248 const char *preamble = "/*\n"
    249 " * Copyright (C) 2015 The Android Open Source Project\n"
    250 " *\n"
    251 " * Licensed under the Apache License, Version 2.0 (the \"License\");\n"
    252 " * you may not use this file except in compliance with the License.\n"
    253 " * You may obtain a copy of the License at\n"
    254 " *\n"
    255 " *      http://www.apache.org/licenses/LICENSE-2.0\n"
    256 " *\n"
    257 " * Unless required by applicable law or agreed to in writing, software\n"
    258 " * distributed under the License is distributed on an \"AS IS\" BASIS,\n"
    259 " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
    260 " * See the License for the specific language governing permissions and\n"
    261 " * limitations under the License.\n"
    262 " */\n"
    263 "\n"
    264 "/* Don't edit this file!  It is auto-generated by float16_gen.sh */\n\n"
    265 "package "PACKAGE_NAME";\n\n"
    266 "public class "CLASS_NAME" {\n";
    267 
    268 int main() {
    269   // Print a preamble with copyright and class declaration, followed by the
    270   // input FP16 array, and reference outputs for pair-wise arithmetic
    271   // operations.
    272   printf("%s", preamble);
    273   printInput();
    274 
    275   printReferenceOutput(OUTPUT_ARRAY_ADD, add, 1);
    276   printReferenceOutput(OUTPUT_ARRAY_SUB, subtract, 1);
    277   printReferenceOutput(OUTPUT_ARRAY_MUL, multiply, 1);
    278   printReferenceOutput(OUTPUT_ARRAY_DIV, divide, 3);
    279 
    280   printf("}");
    281 }
    282