blob: 0f5fc2fc213e2fb193093bfd910f2064be623bab [file] [log] [blame]
//
// Copyright (c) 2017 The Khronos Group Inc.
//
// 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.
//
#ifndef TEST_CONFORMANCE_CLCPP_MATH_FUNCS_REFERENCE_HPP
#define TEST_CONFORMANCE_CLCPP_MATH_FUNCS_REFERENCE_HPP
#include <type_traits>
#include <cmath>
#include <limits>
#include "../common.hpp"
namespace reference
{
// Reference functions for OpenCL comparison functions that
// are not already defined in STL.
cl_float maxmag(const cl_float& x, const cl_float& y)
{
if((std::abs)(x) > (std::abs)(y))
{
return x;
}
else if((std::abs)(y) > (std::abs)(x))
{
return y;
}
return (std::fmax)(x, y);
}
cl_float minmag(const cl_float& x, const cl_float& y)
{
if((std::abs)(x) < (std::abs)(y))
{
return x;
}
else if((std::abs)(y) < (std::abs)(x))
{
return y;
}
return (std::fmin)(x, y);
}
// Reference functions for OpenCL exp functions that
// are not already defined in STL.
cl_double exp10(const cl_double& x)
{
// 10^x = exp2( x * log2(10) )
auto log2_10 = (std::log2)(static_cast<long double>(10.0));
cl_double x_log2_10 = static_cast<cl_double>(x * log2_10);
return (std::exp2)(x_log2_10);
}
// Reference functions for OpenCL floating point functions that
// are not already defined in STL.
cl_double2 fract(cl_double x)
{
// Copied from math_brute_force/reference_math.c
cl_double2 r;
if((std::isnan)(x))
{
r.s[0] = std::numeric_limits<cl_double>::quiet_NaN();
r.s[1] = std::numeric_limits<cl_double>::quiet_NaN();
return r;
}
r.s[0] = (std::modf)(x, &(r.s[1]));
if(r.s[0] < 0.0 )
{
r.s[0] = 1.0f + r.s[0];
r.s[1] -= 1.0f;
if( r.s[0] == 1.0f )
r.s[0] = HEX_FLT(+, 1, fffffe, -, 1);
}
return r;
}
cl_double2 remquo(cl_double x, cl_double y)
{
cl_double2 r;
// remquo return the same value that is returned by the
// remainder function
r.s[0] = (std::remainder)(x,y);
// calulcate quo
cl_double x_y = (x - r.s[0]) / y;
cl_uint quo = (std::abs)(x_y);
r.s[1] = quo & 0x0000007fU;
if(x_y < 0.0)
r.s[1] = -r.s[1];
// fix edge cases
if(!(std::isnan)(x) && y == 0.0)
{
r.s[1] = 0;
}
else if((std::isnan)(x) && (std::isnan)(y))
{
r.s[1] = 0;
}
return r;
}
// Reference functions for OpenCL half_math:: functions that
// are not already defined in STL.
cl_double divide(cl_double x, cl_double y)
{
return x / y;
}
cl_double recip(cl_double x)
{
return 1.0 / x;
}
// Reference functions for OpenCL other functions that
// are not already defined in STL.
cl_double mad(cl_double x, cl_double y, cl_double z)
{
return (x * y) + z;
}
// Reference functions for OpenCL power functions that
// are not already defined in STL.
cl_double rsqrt(const cl_double& x)
{
return cl_double(1.0) / ((std::sqrt)(x));
}
cl_double powr(const cl_double& x, const cl_double& y)
{
//powr(x, y) returns NaN for x < 0.
if( x < 0.0 )
return std::numeric_limits<cl_double>::quiet_NaN();
//powr ( x, NaN ) returns the NaN for x >= 0.
//powr ( NaN, y ) returns the NaN.
if((std::isnan)(x) || (std::isnan)(y) )
return std::numeric_limits<cl_double>::quiet_NaN();
if( x == 1.0 )
{
//powr ( +1, +-inf ) returns NaN.
if((std::abs)(y) == INFINITY )
return std::numeric_limits<cl_double>::quiet_NaN();
//powr ( +1, y ) is 1 for finite y. (NaN handled above)
return 1.0;
}
if( y == 0.0 )
{
//powr ( +inf, +-0 ) returns NaN.
//powr ( +-0, +-0 ) returns NaN.
if( x == 0.0 || x == std::numeric_limits<cl_double>::infinity())
return std::numeric_limits<cl_double>::quiet_NaN();
//powr ( x, +-0 ) is 1 for finite x > 0. (x <= 0, NaN, INF already handled above)
return 1.0;
}
if( x == 0.0 )
{
//powr ( +-0, -inf) is +inf.
//powr ( +-0, y ) is +inf for finite y < 0.
if( y < 0.0 )
return std::numeric_limits<cl_double>::infinity();
//powr ( +-0, y ) is +0 for y > 0. (NaN, y==0 handled above)
return 0.0;
}
// x = +inf
if( (std::isinf)(x) )
{
if( y < 0 )
return 0;
return std::numeric_limits<cl_double>::infinity();
}
double fabsx = (std::abs)(x);
double fabsy = (std::abs)(y);
//y = +-inf cases
if( (std::isinf)(fabsy) )
{
if( y < 0.0 )
{
if( fabsx < 1.0 )
return std::numeric_limits<cl_double>::infinity();
return 0;
}
if( fabsx < 1.0 )
return 0.0;
return std::numeric_limits<cl_double>::infinity();
}
return (std::pow)(x, y);
}
cl_double rootn(const cl_double& x, const cl_int n)
{
//rootn (x, 0) returns a NaN.
if(n == 0)
return std::numeric_limits<cl_double>::quiet_NaN();
//rootn ( x, n ) returns a NaN for x < 0 and n is even.
if(x < 0 && 0 == (n & 1))
return std::numeric_limits<cl_double>::quiet_NaN();
if(x == 0.0)
{
if(n > 0)
{
//rootn ( +-0, n ) is +0 for even n > 0.
if(0 == (n & 1))
{
return cl_double(0.0);
}
//rootn ( +-0, n ) is +-0 for odd n > 0.
else
{
return x;
}
}
else
{
//rootn ( +-0, n ) is +inf for even n < 0.
if(0 == ((-n) & 1))
{
return std::numeric_limits<cl_double>::infinity();
}
//rootn ( +-0, n ) is +-inf for odd n < 0.
else
{
return (std::copysign)(
std::numeric_limits<cl_double>::infinity(), x
);
}
}
}
cl_double r = (std::abs)(x);
r = (std::exp2)((std::log2)(r) / static_cast<cl_double>(n));
return (std::copysign)(r, x);
}
// Reference functions for OpenCL trigonometric functions that
// are not already defined in STL.
cl_double acospi(cl_double x)
{
return (std::acos)(x) / CL_M_PI;
}
cl_double asinpi(cl_double x)
{
return (std::asin)(x) / CL_M_PI;
}
cl_double atanpi(cl_double x)
{
return (std::atan)(x) / CL_M_PI;
}
cl_double cospi(cl_double x)
{
return (std::cos)(x * CL_M_PI);
}
cl_double sinpi(cl_double x)
{
return (std::sin)(x * CL_M_PI);
}
cl_double tanpi(cl_double x)
{
return (std::tan)(x * CL_M_PI);
}
cl_double atan2(cl_double x, cl_double y)
{
#if defined(WIN32) || defined(_WIN32)
// Fix edge cases for Windows
if ((std::isinf)(x) && (std::isinf)(y)) {
cl_double retval = (y > 0) ? CL_M_PI_4 : 3.f * CL_M_PI_4;
return (x > 0) ? retval : -retval;
}
#endif // defined(WIN32) || defined(_WIN32)
return (std::atan2)(x, y);
}
cl_double atan2pi(cl_double x, cl_double y)
{
return ::reference::atan2(x, y) / CL_M_PI;
}
cl_double2 sincos(cl_double x)
{
cl_double2 r;
r.s[0] = (std::sin)(x);
r.s[1] = (std::cos)(x);
return r;
}
}
#endif // TEST_CONFORMANCE_CLCPP_MATH_FUNCS_REFERENCE_HPP