blob: 8f6d4a16a852bc45984dcded4b7df3a715fca326 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.
*/
package android.renderscript.cts;
import java.util.Random;
import java.lang.Math;
import java.lang.Float;
import java.lang.Integer;
import java.lang.Short;
import android.content.Context;
import android.content.res.Resources;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.RSRuntimeException;
import android.renderscript.Script;
import android.renderscript.Type;
import android.util.Log;
public class Float16ArithmeticTest extends RSBaseCompute {
private int numInputs = Float16TestData.input.length;
// Allocations to hold float16 input and output
private Allocation mInput;
private Allocation mF16Matrix;
private Allocation mU16Matrix;
// A numInputs * numInputs length 1-D array with data copied from
// mU16Matrix
private short[] output = new short[numInputs * numInputs];
// 16-bit masks for extracting sign, exponent and mantissa bits
private static short SIGN_MASK = (short) 0x8000;
private static short EXPONENT_MASK = (short) 0x7C00;
private static short MANTISSA_MASK = (short) 0x03FF;
// NaN has all exponent bits set to 1 and a non-zero mantissa
private boolean isFloat16NaN(short val) {
return (val & EXPONENT_MASK) == EXPONENT_MASK &&
(val & MANTISSA_MASK) != 0;
}
// Infinity has all exponent bits set to 1 and zeroes in mantissa
private boolean isFloat16Infinite(short val) {
return (val & EXPONENT_MASK) == EXPONENT_MASK &&
(val & MANTISSA_MASK) == 0;
}
// Subnormal numbers have exponent bits set to 0 and a non-zero mantissa
private boolean isFloat16SubNormal(short val) {
return (val & EXPONENT_MASK) == 0 && (val & MANTISSA_MASK) != 0;
}
// Zero has all but the sign bit set to zero
private boolean isFloat16Zero(short val) {
return (val & ~SIGN_MASK) == 0;
}
// Negativity test checks the sign bit
private boolean isFloat16Negative(short val) {
return (val & SIGN_MASK) != 0;
}
// Check if this is a finite, non-zero FP16 value
private boolean isFloat16FiniteNonZero(short val) {
return !isFloat16NaN(val) && !isFloat16Infinite(val) && !isFloat16Zero(val);
}
// Convert FP16 value to float
private float convertFloat16ToFloat(short val) {
// Extract sign, exponent and mantissa
int sign = val & SIGN_MASK;
int exponent = (val & EXPONENT_MASK) >> 10;
int mantissa = val & MANTISSA_MASK;
// 0.<mantissa> = <mantissa> * 2^-10
float mantissaAsFloat = Math.scalb(mantissa, -10);
float result;
if (isFloat16Zero(val))
result = 0.0f;
else if (isFloat16Infinite(val))
result = java.lang.Float.POSITIVE_INFINITY;
else if (isFloat16NaN(val))
result = java.lang.Float.NaN;
else if (isFloat16SubNormal(val)) {
// value is 2^-14 * mantissaAsFloat
result = Math.scalb(1, -14) * mantissaAsFloat;
}
else {
// value is 2^(exponent - 15) * 1.<mantissa>
result = Math.scalb(1, exponent - 15) * (1 + mantissaAsFloat);
}
if (sign != 0)
result = -result;
return result;
}
// Create input, intermediate, and output allocations. Copy input data to
// the input allocation
private void setupTest() {
Element f16 = Element.F16(mRS);
Element u16 = Element.U16(mRS);
Type f16Matrix = Type.createXY(mRS, f16, numInputs, numInputs);
Type u16Matrix = Type.createXY(mRS, u16, numInputs, numInputs);
mInput = Allocation.createSized(mRS, f16, numInputs);
mF16Matrix = Allocation.createTyped(mRS, f16Matrix);
mU16Matrix = Allocation.createTyped(mRS, u16Matrix);
mInput.copyFromUnchecked(Float16TestData.input);
}
// Check the output of performing 'operation' on inputs x and y against the
// reference output in refValues. For special cases like Infinity, NaN and
// zero, use exact comparison. Otherwise, check if the output is within
// the bounds in 'refValues' by converting all values to float.
private boolean checkFloat16Output(int x, int y, short[][][] refValues,
String operation)
{
// Find the input, output and reference values based on the indices
short in1 = Float16TestData.input[x];
short in2 = Float16TestData.input[y];
short out = output[x + y * numInputs];
short lb = refValues[x][y][0];
short ub = refValues[x][y][1];
// Do exact match if the reference value is a special case (Nan, zero
// infinity or their negative equivalents).
if (isFloat16Infinite(lb))
return lb == out;
// NaN can have any non-zero mantissa. Do not use equality check
if (isFloat16NaN(lb))
return isFloat16NaN(out);
// If reference output is zero, test for exact equivalence if at least
// one of the input values is a special-case FP16 value.
if (isFloat16Zero(lb)) {
if (!isFloat16FiniteNonZero(in1) || !isFloat16FiniteNonZero(in2))
return lb == out;
}
float floatLB = convertFloat16ToFloat(lb);
float floatUB = convertFloat16ToFloat(ub);
float floatOut = convertFloat16ToFloat(out);
if (floatOut < floatLB || floatOut > floatUB) {
StringBuilder message = new StringBuilder();
message.append("Incorrect output for float16 " + operation + ":");
message.append("\nInput 1: " + Short.toString(in1));
message.append("\nInput 2: " + Short.toString(in2));
message.append("\nExpected output between: " + Short.toString(lb) +
" and " + Short.toString(ub));
message.append("\nActual output: " + Short.toString(out));
message.append("\nExpected output (in float) between: " +
Float.toString(floatLB) + " and " + Float.toString(floatUB));
message.append("\nActual output: " + Float.toString(floatOut));
assertTrue(message.toString(), false);
}
return true;
}
private boolean checkFloat16Add(int x, int y) {
return checkFloat16Output(x, y, Float16TestData.ReferenceOutputForAdd,
"addition");
}
public void testFloat16Add() {
setupTest();
ScriptC_float16_arithmetic script = new ScriptC_float16_arithmetic(mRS);
script.set_gInput(mInput);
script.forEach_add(mF16Matrix);
script.forEach_bitcast(mF16Matrix, mU16Matrix);
mU16Matrix.copyTo(output);
for (int x = 0; x < numInputs; x ++) {
for (int y = 0; y < numInputs; y ++) {
checkFloat16Add(x, y);
}
}
}
private boolean checkFloat16Sub(int x, int y) {
return checkFloat16Output(x, y, Float16TestData.ReferenceOutputForSub,
"subtraction");
}
public void testFloat16Sub() {
setupTest();
ScriptC_float16_arithmetic script = new ScriptC_float16_arithmetic(mRS);
script.set_gInput(mInput);
script.forEach_sub(mF16Matrix);
script.forEach_bitcast(mF16Matrix, mU16Matrix);
mU16Matrix.copyTo(output);
for (int x = 0; x < numInputs; x ++) {
for (int y = 0; y < numInputs; y ++) {
checkFloat16Sub(x, y);
}
}
}
private boolean checkFloat16Mul(int x, int y) {
return checkFloat16Output(x, y, Float16TestData.ReferenceOutputForMul,
"multiplication");
}
public void testFloat16Mul() {
setupTest();
ScriptC_float16_arithmetic script = new ScriptC_float16_arithmetic(mRS);
script.set_gInput(mInput);
script.forEach_mul(mF16Matrix);
script.forEach_bitcast(mF16Matrix, mU16Matrix);
mU16Matrix.copyTo(output);
for (int x = 0; x < numInputs; x ++) {
for (int y = 0; y < numInputs; y ++) {
checkFloat16Mul(x, y);
}
}
}
private boolean checkFloat16Div(int x, int y) {
return checkFloat16Output(x, y, Float16TestData.ReferenceOutputForDiv,
"division");
}
public void testFloat16Div() {
setupTest();
ScriptC_float16_arithmetic script = new ScriptC_float16_arithmetic(mRS);
script.set_gInput(mInput);
script.forEach_div(mF16Matrix);
script.forEach_bitcast(mF16Matrix, mU16Matrix);
mU16Matrix.copyTo(output);
for (int x = 0; x < numInputs; x ++) {
for (int y = 0; y < numInputs; y ++) {
checkFloat16Div(x, y);
}
}
}
}