blob: 0daaccc85b15f748357461563c6265f9cfdc8452 [file] [edit]
//===--- rounding.c --Tests for changing rounding mode ----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Checks setting/getting rounding mode using builtin functions
// __builtin_flt_rounds and __builtin_set_flt_rounds.
//
//===----------------------------------------------------------------------===//
#include "rounding.h"
#include <stdio.h>
#ifdef FLOAT_AVAILABLE
// The "base" even values. They can be represented exactly and the lest
// significant bit of the significand is zero.
float PosEvenF = 0x1p+0F;
float NegEvenF = -0x1p+0F;
// The "base" odd values. They can be represented exactly and the lest
// significant bit of the significand is one.
float PosOddF = 0x1.000002p+0F;
float NegOddF = -0x1.000002p+0F;
// The values closest to the base ones with higher magnitude.
float NextToPosEvenF = 0x1.000002p+0F;
float NextToNegEvenF = -0x1.000002p+0F;
float NextToPosOddF = 0x1.000004p+0F;
float NextToNegOddF = -0x1.000004p+0F;
// Value that is half of the lest significant bit of the "base" value.
float PosDeltaF = 0x1.0p-24F;
float NegDeltaF = -0x1.0p-24F;
#endif
#ifdef DOUBLE_AVAILABLE
double PosEvenD = 0x1p+0;
double NegEvenD = -0x1p+0;
double PosOddD = 0x1.0000000000001p+0;
double NegOddD = -0x1.0000000000001p+0;
double NextToPosEvenD = 0x1.0000000000001p+0;
double NextToNegEvenD = -0x1.0000000000001p+0;
double NextToPosOddD = 0x1.0000000000002p+0;
double NextToNegOddD = -0x1.0000000000002p+0;
double PosDeltaD = 0x1.0p-52;
double NegDeltaD = -0x1.0p-52;
#endif
// Check macros. They may be overriden to implement different reaction on
// broken condition, rather that printing a message.
#ifndef REPORT_STAGE
#define REPORT_STAGE(x) printf(x)
#endif
#ifndef CHECK_INT_EQ
#define CHECK_INT_EQ(VExp, VAct) \
if (VExp != VAct) { \
printf("Failure on line %i - expected: %d, actual: %d\n", \
__LINE__, VExp, VAct); \
}
#endif
#ifdef FLOAT_AVAILABLE
#ifndef CHECK_FLT_EQ
#define CHECK_FLT_EQ(VExp, VAct) \
if (VExp != VAct) { \
printf("Failure on line %i - expected: %a, actual: %a\n", \
__LINE__, VExp, VAct); \
}
#endif
#endif
#ifdef DOUBLE_AVAILABLE
#ifndef CHECK_DBL_EQ
#define CHECK_DBL_EQ(VExp, VAct) \
if (VExp != VAct) { \
printf("Failure on line %i - expected: %la, actual: %la\n", \
__LINE__, VExp, VAct); \
}
#endif
#endif
#ifdef FLOAT_AVAILABLE
static void check_float_toward_zero() {
float Res;
Res = PosEvenF + PosDeltaF; // Rounds down
CHECK_FLT_EQ(PosEvenF, Res);
Res = NegEvenF + NegDeltaF; // Rounsd up
CHECK_FLT_EQ(NegEvenF, Res);
Res = PosOddF + PosDeltaF; // Rounsd down
CHECK_FLT_EQ(PosOddF, Res);
Res = NegOddF + NegDeltaF; // Rounsd up
CHECK_FLT_EQ(NegOddF, Res);
}
static void check_float_toward_nearest() {
float Res;
Res = PosEvenF + PosDeltaF; // Rounsd down
CHECK_FLT_EQ(PosEvenF, Res);
Res = NegEvenF + NegDeltaF; // Pounds up
CHECK_FLT_EQ(NegEvenF, Res);
Res = PosOddF + PosDeltaF; // Rounsd up
CHECK_FLT_EQ(NextToPosOddF, Res);
Res = NegOddF + NegDeltaF; // Rounds down
CHECK_FLT_EQ(NextToNegOddF, Res);
}
static void check_float_upward() {
float Res;
Res = PosEvenF + PosDeltaF; // Rounsd up
CHECK_FLT_EQ(NextToPosEvenF, Res);
Res = NegEvenF + NegDeltaF; // Pounds up
CHECK_FLT_EQ(NegEvenF, Res);
Res = PosOddF + PosDeltaF; // Rounsd up
CHECK_FLT_EQ(NextToPosOddF, Res);
Res = NegOddF + NegDeltaF; // Rounds up
CHECK_FLT_EQ(NegOddF, Res);
}
static void check_float_downward() {
float Res;
Res = PosEvenF + PosDeltaF; // Rounsd down
CHECK_FLT_EQ(PosEvenF, Res);
Res = NegEvenF + NegDeltaF; // Pounds down
CHECK_FLT_EQ(NextToNegEvenF, Res);
Res = PosOddF + PosDeltaF; // Rounsd down
CHECK_FLT_EQ(PosOddF, Res);
Res = NegOddF + NegDeltaF; // Rounds down
CHECK_FLT_EQ(NextToNegOddF, Res);
}
static void check_float() {
#pragma STDC FENV_ACCESS ON
REPORT_STAGE("Checking float\n");
float Res;
int RM;
REPORT_STAGE(" towardzero\n");
__builtin_set_flt_rounds(ROUNDING_TOWARDZERO);
RM = __builtin_flt_rounds();
CHECK_INT_EQ(ROUNDING_TOWARDZERO, RM);
check_float_toward_zero();
REPORT_STAGE(" tonearest\n");
__builtin_set_flt_rounds(ROUNDING_TONEAREST);
RM = __builtin_flt_rounds();
CHECK_INT_EQ(ROUNDING_TONEAREST, RM);
check_float_toward_nearest();
REPORT_STAGE(" upward\n");
__builtin_set_flt_rounds(ROUNDING_UPWARD);
RM = __builtin_flt_rounds();
CHECK_INT_EQ(ROUNDING_UPWARD, RM);
check_float_upward();
REPORT_STAGE(" downward\n");
__builtin_set_flt_rounds(ROUNDING_DOWNWARD);
RM = __builtin_flt_rounds();
CHECK_INT_EQ(ROUNDING_DOWNWARD, RM);
check_float_downward();
REPORT_STAGE(" towardzero (dynamic)\n");
RM = get_rounding_mode(ROUNDING_TOWARDZERO);
__builtin_set_flt_rounds(RM);
RM = __builtin_flt_rounds();
CHECK_INT_EQ(ROUNDING_TOWARDZERO, RM);
check_float_toward_zero();
REPORT_STAGE(" tonearest (dynamic)\n");
RM = get_rounding_mode(ROUNDING_TONEAREST);
__builtin_set_flt_rounds(RM);
RM = __builtin_flt_rounds();
CHECK_INT_EQ(ROUNDING_TONEAREST, RM);
check_float_toward_nearest();
REPORT_STAGE(" upward (dynamic)\n");
RM = get_rounding_mode(ROUNDING_UPWARD);
__builtin_set_flt_rounds(RM);
RM = __builtin_flt_rounds();
CHECK_INT_EQ(ROUNDING_UPWARD, RM);
check_float_upward();
REPORT_STAGE(" downward (dynamic)\n");
RM = get_rounding_mode(ROUNDING_DOWNWARD);
__builtin_set_flt_rounds(RM);
RM = __builtin_flt_rounds();
CHECK_INT_EQ(ROUNDING_DOWNWARD, RM);
check_float_downward();
}
#else
static void check_float() {}
#endif
#ifdef DOUBLE_AVAILABLE
static void check_double_toward_zero() {
double Res;
Res = PosEvenF + PosDeltaF; // Rounds down
CHECK_DBL_EQ(PosEvenF, Res);
Res = NegEvenF + NegDeltaF; // Rounsd up
CHECK_DBL_EQ(NegEvenF, Res);
Res = PosOddF + PosDeltaF; // Rounsd down
CHECK_DBL_EQ(PosOddF, Res);
Res = NegOddF + NegDeltaF; // Rounsd up
CHECK_DBL_EQ(NegOddF, Res);
}
static void check_double_toward_nearest() {
double Res;
Res = PosEvenF + PosDeltaF; // Rounsd down
CHECK_DBL_EQ(PosEvenF, Res);
Res = NegEvenF + NegDeltaF; // Pounds up
CHECK_DBL_EQ(NegEvenF, Res);
Res = PosOddF + PosDeltaF; // Rounsd up
CHECK_DBL_EQ(NextToPosOddF, Res);
Res = NegOddF + NegDeltaF; // Rounds down
CHECK_DBL_EQ(NextToNegOddF, Res);
}
static void check_double_upward() {
double Res;
Res = PosEvenF + PosDeltaF; // Rounsd up
CHECK_DBL_EQ(NextToPosEvenF, Res);
Res = NegEvenF + NegDeltaF; // Pounds up
CHECK_DBL_EQ(NegEvenF, Res);
Res = PosOddF + PosDeltaF; // Rounsd up
CHECK_DBL_EQ(NextToPosOddF, Res);
Res = NegOddF + NegDeltaF; // Rounds up
CHECK_DBL_EQ(NegOddF, Res);
}
static void check_double_downward() {
double Res;
Res = PosEvenF + PosDeltaF; // Rounsd down
CHECK_DBL_EQ(PosEvenF, Res);
Res = NegEvenF + NegDeltaF; // Pounds down
CHECK_DBL_EQ(NextToNegEvenF, Res);
Res = PosOddF + PosDeltaF; // Rounsd down
CHECK_DBL_EQ(PosOddF, Res);
Res = NegOddF + NegDeltaF; // Rounds down
CHECK_DBL_EQ(NextToNegOddF, Res);
}
static void check_double() {
#pragma STDC FENV_ACCESS ON
REPORT_STAGE("Checking double\n");
double Res;
int RM;
REPORT_STAGE(" towardzero\n");
__builtin_set_flt_rounds(ROUNDING_TOWARDZERO);
RM = __builtin_flt_rounds();
CHECK_INT_EQ(ROUNDING_TOWARDZERO, RM);
check_double_toward_zero();
REPORT_STAGE(" tonearest\n");
__builtin_set_flt_rounds(ROUNDING_TONEAREST);
RM = __builtin_flt_rounds();
CHECK_INT_EQ(ROUNDING_TONEAREST, RM);
check_double_toward_nearest();
REPORT_STAGE(" upward\n");
__builtin_set_flt_rounds(ROUNDING_UPWARD);
RM = __builtin_flt_rounds();
CHECK_INT_EQ(ROUNDING_UPWARD, RM);
check_double_upward();
REPORT_STAGE(" downward\n");
__builtin_set_flt_rounds(ROUNDING_DOWNWARD);
RM = __builtin_flt_rounds();
CHECK_INT_EQ(ROUNDING_DOWNWARD, RM);
check_double_downward();
REPORT_STAGE(" towardzero (dynamic)\n");
RM = get_rounding_mode(ROUNDING_TOWARDZERO);
__builtin_set_flt_rounds(RM);
RM = __builtin_flt_rounds();
CHECK_INT_EQ(ROUNDING_TOWARDZERO, RM);
check_double_toward_zero();
REPORT_STAGE(" tonearest (dynamic)\n");
RM = get_rounding_mode(ROUNDING_TONEAREST);
__builtin_set_flt_rounds(RM);
RM = __builtin_flt_rounds();
CHECK_INT_EQ(ROUNDING_TONEAREST, RM);
check_double_toward_nearest();
REPORT_STAGE(" upward (dynamic)\n");
RM = get_rounding_mode(ROUNDING_UPWARD);
__builtin_set_flt_rounds(RM);
RM = __builtin_flt_rounds();
CHECK_INT_EQ(ROUNDING_UPWARD, RM);
check_double_upward();
REPORT_STAGE(" downward (dynamic)\n");
RM = get_rounding_mode(ROUNDING_DOWNWARD);
__builtin_set_flt_rounds(RM);
RM = __builtin_flt_rounds();
CHECK_INT_EQ(ROUNDING_DOWNWARD, RM);
check_double_downward();
}
#else
static void check_double() {}
#endif
int main(int argc, char *argv[]) {
REPORT_STAGE("Checking rounding\n");
check_float();
check_double();
}