blob: 3d493f6bdb8dc4290bbd43b77dfb1e6d6baebdb3 [file] [edit]
/*
* Copyright (c) 2014 The Native Client Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <errno.h>
#include <float.h>
#include <math.h>
#include <stdio.h>
/*
* We want to be able to test LLVM's frem instruction without linking in fmod
* at bitcode linking time to ensure that the native library has a
* functioning copy of fmod. However, we also want to test that the bitcode
* library's fmod also works. So we cannot test both fmod and frem in the
* same build of the pexe, and instead build and test twice.
*/
#if defined(TEST_LLVM_IR)
extern double frem(double, double);
extern float fremf(float, float);
#define fmod frem
#define fmodf fremf
#endif
#define CHECK_ERRNO(expected) \
do { \
if (expected != errno) { \
fprintf(stderr, "ERROR(%d): errno %d != %d\n", \
__LINE__, expected, errno); \
err_count++; \
} \
/* Reset errno to something predictable. */ \
errno = 0; \
} while(0)
#define CHECK_NAN(err, numer, denom) \
do { \
double res = fmod(numer, denom); \
CHECK_ERRNO(err); \
if (!isnan(res)) { \
fprintf(stderr, "ERROR(%d): !isnan(%f mod %f) == %f\n", \
__LINE__, numer, denom, res); \
err_count++; \
} \
} while(0)
#define CHECK_NANF(err, numer, denom) \
do { \
float res = fmodf(numer, denom); \
CHECK_ERRNO(err); \
if (!isnan(res)) { \
fprintf(stderr, "ERROR(%d): !isnan(%f modf %f) == %f\n", \
__LINE__, numer, denom, res); \
err_count++; \
} \
} while(0)
const double kTolerance = DBL_EPSILON;
const double kToleranceF = FLT_EPSILON * 2;
#define CHECK_EQ(expect, numer, denom) \
do { \
double res = fmod(numer, denom); \
CHECK_ERRNO(0); \
/* The tolerance check may not work for SUBNORMAL, NaN, etc., so check */ \
if (!(fpclassify(res) == FP_NORMAL || fpclassify(res) == FP_ZERO)) { \
fprintf(stderr, "ERROR(%d): result is not normal/zero %f\n", \
__LINE__, res); \
err_count++; \
} \
if (fabs(expect - res) > kTolerance) { \
fprintf(stderr, "ERROR: %f mod %f == %f, != %f\n", \
numer, denom, res, expect); \
err_count++; \
} \
} while(0)
#define CHECK_EQF(expect, numer, denom) \
do { \
float res = fmodf(numer, denom); \
CHECK_ERRNO(0); \
/* The tolerance check may not work for SUBNORMAL, NaN, etc., so check */ \
if (!(fpclassify(res) == FP_NORMAL || fpclassify(res) == FP_ZERO)) { \
fprintf(stderr, "ERROR(%d): result is not normal/zero %f\n", \
__LINE__, res); \
err_count++; \
} \
if (fabsf(expect - res) > kToleranceF) { \
fprintf(stderr, "ERROR: %f modf %f == %f, != %f\n", \
numer, denom, res, expect); \
err_count++; \
} \
} while(0)
int main(void) {
/* Set up some volatile constants to block the optimizer. */
volatile double zero = 0.0;
volatile double nan = NAN;
volatile double two = 2.0;
volatile double onesix = 1.6;
volatile double infinity = INFINITY;
int err_count = 0;
#if defined(TEST_LLVM_IR)
/* With the LLVM IR frem instruction, errno is never set
* (see the PNaCl bitcode ABI documentation).
*/
int expected_errno_infinity = 0;
int expected_errno_zerodiv = 0;
#elif defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 10
/*
* The older (pre 2.10) glibc and newlib don't set errno when x is infinity.
* See "BUGS" under the fmod manpage. It only sets errno for divide by zero.
*/
int expected_errno_infinity = EDOM;
int expected_errno_zerodiv = EDOM;
#else
int expected_errno_infinity = 0;
int expected_errno_zerodiv = EDOM;
#endif
/* Initialize errno to something predictable. */
errno = 0;
/* If x or y is a NaN, a NaN is returned. */
CHECK_NAN(0, nan, two);
CHECK_NANF(0, (float)nan, (float)two);
CHECK_NAN(0, two, nan);
CHECK_NANF(0, (float)two, (float)nan);
CHECK_NAN(0, -onesix, nan);
CHECK_NANF(0, (float)-onesix, (float)nan);
CHECK_NAN(0, nan, nan);
CHECK_NANF(0, (float)nan, (float)nan);
CHECK_NAN(0, nan, infinity);
CHECK_NANF(0, (float)nan, (float)infinity);
/* If x is infinity, a NaN is returned and errno is
* expected_errno_infinity (see note about BUGS).
*/
CHECK_NAN(expected_errno_infinity, infinity, two);
CHECK_NANF(expected_errno_infinity, (float)infinity, (float)two);
CHECK_NAN(expected_errno_infinity, -infinity, two);
CHECK_NANF(expected_errno_infinity, (float)-infinity, (float)two);
/* If y is zero, a NaN is returned and errno is expected_errno_zerodiv. */
CHECK_NAN(expected_errno_zerodiv, two, zero);
CHECK_NANF(expected_errno_zerodiv, (float)two, (float)zero);
CHECK_NAN(expected_errno_zerodiv, two, -zero);
CHECK_NANF(expected_errno_zerodiv, (float)two, (float)-zero);
CHECK_NAN(expected_errno_zerodiv, infinity, zero);
CHECK_NANF(expected_errno_zerodiv, (float)infinity, (float)zero);
CHECK_NAN(expected_errno_zerodiv, infinity, -zero);
CHECK_NANF(expected_errno_zerodiv, (float)infinity, (float)-zero);
/* If x is +0 (-0), and y is not zero, a +0 (-0) is returned. */
CHECK_EQ(zero, zero, two);
CHECK_EQF((float)zero, (float)zero, (float)two);
CHECK_EQ(zero, zero, -two);
CHECK_EQF((float)zero, (float)zero, (float)-two);
CHECK_EQ(-zero, -zero, two);
CHECK_EQF((float)-zero, (float)-zero, (float)two);
CHECK_EQ(-zero, -zero, -two);
CHECK_EQF((float)-zero, (float)-zero, (float)-two);
/*
* On success... the returned value has the same sign as x and a magnitude
* less than the magnitude of y.
*/
CHECK_EQ(1.2, 5.2, two);
CHECK_EQF(1.2f, 5.2f, (float)two);
CHECK_EQ(-0.6, -0.6, two);
CHECK_EQF(-0.6f, -0.6f, (float)two);
CHECK_EQ(-0.6, -0.6, -two);
CHECK_EQF(-0.6f, -0.6f, (float)-two);
CHECK_EQ(zero, 6.4, onesix);
CHECK_EQF((float)zero, 6.4, (float)onesix);
CHECK_EQ(1.0, 5.0, two);
CHECK_EQF(1.0f, 5.0, (float)two);
CHECK_EQ(-1.0, -5.0, two);
CHECK_EQF(-1.0f, -5.0, (float)two);
CHECK_EQ(zero, 100.0, two);
CHECK_EQF((float)zero, 100.0, (float)two);
CHECK_EQ(-zero, -100.0, two);
CHECK_EQF((float)-zero, -100.0, (float)two);
/* If the numerator is finite and the denominator is an infinity, the
* result is the numerator.
*/
CHECK_EQ(5.2, 5.2, infinity);
CHECK_EQF(5.2f, 5.2f, (float)infinity);
CHECK_EQ(5.2, 5.2, -infinity);
CHECK_EQF(5.2f, 5.2f, (float)-infinity);
CHECK_EQ(-5.2, -5.2, infinity);
CHECK_EQF(-5.2f, -5.2f, (float)infinity);
fprintf(stderr, "Total of %d errors\n", err_count);
return err_count;
}