blob: 433ee16ef42e9072ccf92d3962a12d0b685368c0 [file] [log] [blame]
/*
* Copyright (c) 2013 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.
*/
/*
* Test rounding behavior, checking and setting the rounding mode.
*/
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
volatile double two = 2.0;
volatile double one = 1.0;
volatile double zero = 0.0;
volatile double neg_one = -1.0;
volatile double neg_two = -2.0;
#define ASSERT_TRUE(pred) \
if (!pred) { \
fprintf(stderr, "file %s, line %d, !(" #pred ")\n", \
__FILE__, __LINE__); \
}
#define ASSERT_EQ(f1, f2) \
do { \
double x = f1; \
double y = f2; \
if (x != y) { \
fprintf(stderr, "file %s, line %d, %f != %f\n", \
__FILE__, __LINE__, x, y); \
abort(); \
} \
} while(0)
void __attribute__((noinline)) test_flt_rounds(int);
void test_flt_rounds(int expected_round) {
ASSERT_EQ(FLT_ROUNDS, expected_round);
}
extern void set_round_toward_nearest(void);
extern void set_round_toward_plus_infinity(void);
extern void set_round_toward_minus_infinity(void);
extern void set_round_toward_zero(void);
/* Test that nearbyint() really does round to nearest. */
void __attribute__((noinline)) test_round_to_nearest(void);
void test_round_to_nearest(void) {
ASSERT_EQ(nearbyint(one + 0.5), two);
ASSERT_EQ(nearbyint(zero + 0.5), zero);
ASSERT_EQ(nearbyint(zero), zero);
ASSERT_EQ(nearbyint(-zero), -zero);
ASSERT_EQ(nearbyint(neg_one + 0.5), -zero);
ASSERT_TRUE(isnan(nearbyint(NAN)));
ASSERT_EQ(nearbyint(INFINITY), INFINITY);
}
/* Test that nearbyint() really does round to plus infinity. */
void __attribute__((noinline)) test_round_to_plus_infinity(void);
void test_round_to_plus_infinity(void) {
ASSERT_EQ(nearbyint(one + 0.5), two);
ASSERT_EQ(nearbyint(zero + 0.5), one);
ASSERT_EQ(nearbyint(zero), zero);
ASSERT_EQ(nearbyint(-zero), -zero);
ASSERT_EQ(nearbyint(neg_one + 0.5), -zero);
ASSERT_TRUE(isnan(nearbyint(NAN)));
ASSERT_EQ(nearbyint(INFINITY), INFINITY);
}
/* Test that nearbyint() really does round to minus infinity. */
void __attribute__((noinline)) test_round_to_minus_infinity(void);
void test_round_to_minus_infinity(void) {
ASSERT_EQ(nearbyint(one + 0.5), one);
ASSERT_EQ(nearbyint(zero + 0.5), zero);
ASSERT_EQ(nearbyint(zero), zero);
ASSERT_EQ(nearbyint(-zero), -zero);
ASSERT_EQ(nearbyint(neg_one + 0.5), neg_one);
ASSERT_TRUE(isnan(nearbyint(NAN)));
ASSERT_EQ(nearbyint(INFINITY), INFINITY);
}
/* Test that nearbyint() really does round to zero. */
void __attribute__((noinline)) test_round_to_zero(void);
void test_round_to_zero(void) {
ASSERT_EQ(nearbyint(one + 0.5), one);
ASSERT_EQ(nearbyint(zero + 0.5), zero);
ASSERT_EQ(nearbyint(zero), zero);
ASSERT_EQ(nearbyint(-zero), -zero);
ASSERT_EQ(nearbyint(neg_one + 0.5), -zero);
ASSERT_TRUE(isnan(nearbyint(NAN)));
ASSERT_EQ(nearbyint(INFINITY), INFINITY);
}
int main(int ac, char* av[]) {
#if defined(__mips__)
/* For MIPS, the llvm.flt.rounds intrinsic is stuck at (1), so we cannot
* test the other cases. Furthermore, the llvm MIPS asm parser doesn't
* parse the cfc1 $reg, $31 and ctc1 instructions for setting
* the rounding mode.
*
* At least test that initial value is always round towards nearest.
*/
test_flt_rounds(1);
test_round_to_nearest();
#else
/* Test that initial value is always round towards nearest. */
test_flt_rounds(1);
test_round_to_nearest();
set_round_toward_plus_infinity();
/*
* GCC + Newlib and Glibc always return 1 for FLT_ROUNDS,
* so we can't really test FLT_ROUNDS. However, we can observe the
* difference through nearbyint() behavior:
* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30569
*/
#if defined(__clang__)
test_flt_rounds(2);
#endif
test_round_to_plus_infinity();
set_round_toward_minus_infinity();
#if defined(__clang__)
test_flt_rounds(3);
#endif
test_round_to_minus_infinity();
set_round_toward_zero();
#if defined(__clang__)
test_flt_rounds(0);
#endif
test_round_to_zero();
set_round_toward_nearest();
#endif /* __mips__ */
test_flt_rounds(1);
test_round_to_nearest();
return 0;
}