| Elliott Hughes | 90e10d4 | 2012-11-02 17:05:20 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2012 The Android Open Source Project | 
 | 3 |  * | 
 | 4 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 |  * you may not use this file except in compliance with the License. | 
 | 6 |  * You may obtain a copy of the License at | 
 | 7 |  * | 
 | 8 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 |  * | 
 | 10 |  * Unless required by applicable law or agreed to in writing, software | 
 | 11 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 |  * See the License for the specific language governing permissions and | 
 | 14 |  * limitations under the License. | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | #include <gtest/gtest.h> | 
 | 18 |  | 
| Elliott Hughes | 18a1957 | 2017-11-02 16:18:43 -0700 | [diff] [blame] | 19 | #include "utils.h" | 
 | 20 |  | 
| Elliott Hughes | 90e10d4 | 2012-11-02 17:05:20 -0700 | [diff] [blame] | 21 | #include <fenv.h> | 
 | 22 | #include <stdint.h> | 
 | 23 |  | 
 | 24 | static void TestRounding(float expectation1, float expectation2) { | 
 | 25 |   // volatile to prevent compiler optimizations. | 
 | 26 |   volatile float f = 1.968750f; | 
 | 27 |   volatile float m = 0x1.0p23f; | 
 | 28 |   volatile float x = f + m; | 
 | 29 |   ASSERT_FLOAT_EQ(expectation1, x); | 
 | 30 |   x -= m; | 
 | 31 |   ASSERT_EQ(expectation2, x); | 
 | 32 | } | 
 | 33 |  | 
 | 34 | static void DivideByZero() { | 
 | 35 |   // volatile to prevent compiler optimizations. | 
 | 36 |   volatile float zero = 0.0f; | 
 | 37 |   volatile float result __attribute__((unused)) = 123.0f / zero; | 
 | 38 | } | 
 | 39 |  | 
 | 40 | TEST(fenv, fesetround_fegetround_FE_TONEAREST) { | 
 | 41 |   fesetround(FE_TONEAREST); | 
 | 42 |   ASSERT_EQ(FE_TONEAREST, fegetround()); | 
 | 43 |   TestRounding(8388610.0f, 2.0f); | 
 | 44 | } | 
 | 45 |  | 
 | 46 | TEST(fenv, fesetround_fegetround_FE_TOWARDZERO) { | 
 | 47 |   fesetround(FE_TOWARDZERO); | 
 | 48 |   ASSERT_EQ(FE_TOWARDZERO, fegetround()); | 
 | 49 |   TestRounding(8388609.0f, 1.0f); | 
 | 50 | } | 
 | 51 |  | 
 | 52 | TEST(fenv, fesetround_fegetround_FE_UPWARD) { | 
 | 53 |   fesetround(FE_UPWARD); | 
 | 54 |   ASSERT_EQ(FE_UPWARD, fegetround()); | 
 | 55 |   TestRounding(8388610.0f, 2.0f); | 
 | 56 | } | 
 | 57 |  | 
 | 58 | TEST(fenv, fesetround_fegetround_FE_DOWNWARD) { | 
 | 59 |   fesetround(FE_DOWNWARD); | 
 | 60 |   ASSERT_EQ(FE_DOWNWARD, fegetround()); | 
 | 61 |   TestRounding(8388609.0f, 1.0f); | 
 | 62 | } | 
 | 63 |  | 
 | 64 | TEST(fenv, feclearexcept_fetestexcept) { | 
 | 65 |   // Clearing clears. | 
 | 66 |   feclearexcept(FE_ALL_EXCEPT); | 
 | 67 |   ASSERT_EQ(0, fetestexcept(FE_ALL_EXCEPT)); | 
 | 68 |  | 
 | 69 |   // Dividing by zero sets FE_DIVBYZERO. | 
 | 70 |   DivideByZero(); | 
 | 71 |   int raised = fetestexcept(FE_DIVBYZERO | FE_OVERFLOW); | 
 | 72 |   ASSERT_TRUE((raised & FE_OVERFLOW) == 0); | 
 | 73 |   ASSERT_TRUE((raised & FE_DIVBYZERO) != 0); | 
 | 74 |  | 
 | 75 |   // Clearing an unset bit is a no-op. | 
 | 76 |   feclearexcept(FE_OVERFLOW); | 
 | 77 |   ASSERT_TRUE((raised & FE_OVERFLOW) == 0); | 
 | 78 |   ASSERT_TRUE((raised & FE_DIVBYZERO) != 0); | 
 | 79 |  | 
 | 80 |   // Clearing a set bit works. | 
 | 81 |   feclearexcept(FE_DIVBYZERO); | 
 | 82 |   ASSERT_EQ(0, fetestexcept(FE_ALL_EXCEPT)); | 
 | 83 | } | 
| Elliott Hughes | a0ee078 | 2013-01-30 19:06:37 -0800 | [diff] [blame] | 84 |  | 
 | 85 | TEST(fenv, FE_DFL_ENV_macro) { | 
 | 86 |   ASSERT_EQ(0, fesetenv(FE_DFL_ENV)); | 
 | 87 | } | 
| Elliott Hughes | 18a1957 | 2017-11-02 16:18:43 -0700 | [diff] [blame] | 88 |  | 
 | 89 | TEST(fenv, feraiseexcept) { | 
 | 90 |   feclearexcept(FE_ALL_EXCEPT); | 
 | 91 |   ASSERT_EQ(0, fetestexcept(FE_ALL_EXCEPT)); | 
 | 92 |  | 
 | 93 |   ASSERT_EQ(0, feraiseexcept(FE_DIVBYZERO | FE_OVERFLOW)); | 
 | 94 |   ASSERT_EQ(FE_DIVBYZERO | FE_OVERFLOW, fetestexcept(FE_ALL_EXCEPT)); | 
 | 95 | } | 
 | 96 |  | 
 | 97 | TEST(fenv, fegetenv_fesetenv) { | 
 | 98 |   // Set FE_OVERFLOW only. | 
 | 99 |   feclearexcept(FE_ALL_EXCEPT); | 
 | 100 |   ASSERT_EQ(0, fetestexcept(FE_ALL_EXCEPT)); | 
 | 101 |   ASSERT_EQ(0, feraiseexcept(FE_OVERFLOW)); | 
 | 102 |  | 
 | 103 |   // fegetenv (unlike feholdexcept) leaves the current state untouched... | 
 | 104 |   fenv_t state; | 
 | 105 |   ASSERT_EQ(0, fegetenv(&state)); | 
 | 106 |   ASSERT_EQ(FE_OVERFLOW, fetestexcept(FE_ALL_EXCEPT)); | 
 | 107 |  | 
 | 108 |   // Dividing by zero sets the appropriate flag... | 
 | 109 |   DivideByZero(); | 
 | 110 |   ASSERT_EQ(FE_DIVBYZERO | FE_OVERFLOW, fetestexcept(FE_ALL_EXCEPT)); | 
 | 111 |  | 
 | 112 |   // And fesetenv (unlike feupdateenv) clobbers that to return to where | 
 | 113 |   // we started. | 
 | 114 |   ASSERT_EQ(0, fesetenv(&state)); | 
 | 115 |   ASSERT_EQ(FE_OVERFLOW, fetestexcept(FE_ALL_EXCEPT)); | 
 | 116 | } | 
 | 117 |  | 
 | 118 | TEST(fenv, feholdexcept_feupdateenv) { | 
 | 119 |   // Set FE_OVERFLOW only. | 
 | 120 |   feclearexcept(FE_ALL_EXCEPT); | 
 | 121 |   ASSERT_EQ(0, fetestexcept(FE_ALL_EXCEPT)); | 
 | 122 |   ASSERT_EQ(0, feraiseexcept(FE_OVERFLOW)); | 
 | 123 |  | 
 | 124 |   // feholdexcept (unlike fegetenv) clears everything... | 
 | 125 |   fenv_t state; | 
 | 126 |   ASSERT_EQ(0, feholdexcept(&state)); | 
 | 127 |   ASSERT_EQ(0, fetestexcept(FE_ALL_EXCEPT)); | 
 | 128 |  | 
 | 129 |   // Dividing by zero sets the appropriate flag... | 
 | 130 |   DivideByZero(); | 
 | 131 |   ASSERT_EQ(FE_DIVBYZERO, fetestexcept(FE_ALL_EXCEPT)); | 
 | 132 |  | 
 | 133 |   // And feupdateenv (unlike fesetenv) merges what we started with | 
 | 134 |   // (FE_OVERFLOW) with what we now have (FE_DIVBYZERO). | 
 | 135 |   ASSERT_EQ(0, feupdateenv(&state)); | 
 | 136 |   ASSERT_EQ(FE_DIVBYZERO | FE_OVERFLOW, fetestexcept(FE_ALL_EXCEPT)); | 
 | 137 | } | 
 | 138 |  | 
 | 139 | TEST(fenv, fegetexceptflag_fesetexceptflag) { | 
 | 140 |   // Set three flags. | 
 | 141 |   feclearexcept(FE_ALL_EXCEPT); | 
 | 142 |   ASSERT_EQ(0, feraiseexcept(FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW)); | 
 | 143 |   ASSERT_EQ(FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW, fetestexcept(FE_ALL_EXCEPT)); | 
 | 144 |  | 
 | 145 |   fexcept_t all; // FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW | 
 | 146 |   fexcept_t two; // FE_OVERFLOW | FE_UNDERFLOW | 
 | 147 |   ASSERT_EQ(0, fegetexceptflag(&all, FE_ALL_EXCEPT)); | 
 | 148 |   ASSERT_EQ(0, fegetexceptflag(&two, FE_OVERFLOW | FE_UNDERFLOW)); | 
 | 149 |  | 
 | 150 |   // Check we can restore all. | 
 | 151 |   feclearexcept(FE_ALL_EXCEPT); | 
 | 152 |   ASSERT_EQ(0, fesetexceptflag(&all, FE_ALL_EXCEPT)); | 
 | 153 |   ASSERT_EQ(FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW, fetestexcept(FE_ALL_EXCEPT)); | 
 | 154 |  | 
 | 155 |   // Check that `two` only stored a subset. | 
 | 156 |   feclearexcept(FE_ALL_EXCEPT); | 
 | 157 |   ASSERT_EQ(0, fesetexceptflag(&two, FE_ALL_EXCEPT)); | 
 | 158 |   ASSERT_EQ(FE_OVERFLOW | FE_UNDERFLOW, fetestexcept(FE_ALL_EXCEPT)); | 
 | 159 |  | 
 | 160 |   // Check that we can restore a single flag. | 
 | 161 |   feclearexcept(FE_ALL_EXCEPT); | 
 | 162 |   ASSERT_EQ(0, fesetexceptflag(&all, FE_DIVBYZERO)); | 
 | 163 |   ASSERT_EQ(FE_DIVBYZERO, fetestexcept(FE_ALL_EXCEPT)); | 
 | 164 |  | 
 | 165 |   // Check that we can restore a subset of flags. | 
 | 166 |   feclearexcept(FE_ALL_EXCEPT); | 
 | 167 |   ASSERT_EQ(0, fesetexceptflag(&all, FE_OVERFLOW | FE_UNDERFLOW)); | 
 | 168 |   ASSERT_EQ(FE_OVERFLOW | FE_UNDERFLOW, fetestexcept(FE_ALL_EXCEPT)); | 
 | 169 | } | 
 | 170 |  | 
 | 171 | TEST(fenv, fedisableexcept_fegetexcept) { | 
 | 172 |   feclearexcept(FE_ALL_EXCEPT); | 
 | 173 |   ASSERT_EQ(0, fetestexcept(FE_ALL_EXCEPT)); | 
 | 174 |  | 
 | 175 |   // No SIGFPE please... | 
 | 176 |   ASSERT_EQ(0, fedisableexcept(FE_ALL_EXCEPT)); | 
 | 177 |   ASSERT_EQ(0, fegetexcept()); | 
 | 178 |   ASSERT_EQ(0, feraiseexcept(FE_INVALID)); | 
 | 179 |   ASSERT_EQ(FE_INVALID, fetestexcept(FE_ALL_EXCEPT)); | 
 | 180 | } | 
 | 181 |  | 
 | 182 | TEST(fenv, feenableexcept_fegetexcept) { | 
 | 183 | #if defined(__aarch64__) || defined(__arm__) | 
| Elliott Hughes | b6c7f6e | 2017-11-03 16:46:32 -0700 | [diff] [blame] | 184 |   // ARM doesn't support this. They used to if you go back far enough, but it was removed in | 
 | 185 |   // the Cortex-A8 between r3p1 and r3p2. | 
 | 186 |   ASSERT_EQ(-1, feenableexcept(FE_INVALID)); | 
 | 187 |   ASSERT_EQ(0, fegetexcept()); | 
 | 188 |   ASSERT_EQ(-1, feenableexcept(FE_DIVBYZERO)); | 
 | 189 |   ASSERT_EQ(0, fegetexcept()); | 
 | 190 |   ASSERT_EQ(-1, feenableexcept(FE_OVERFLOW)); | 
 | 191 |   ASSERT_EQ(0, fegetexcept()); | 
 | 192 |   ASSERT_EQ(-1, feenableexcept(FE_UNDERFLOW)); | 
 | 193 |   ASSERT_EQ(0, fegetexcept()); | 
 | 194 |   ASSERT_EQ(-1, feenableexcept(FE_INEXACT)); | 
 | 195 |   ASSERT_EQ(0, fegetexcept()); | 
 | 196 |   ASSERT_EQ(-1, feenableexcept(FE_DENORMAL)); | 
 | 197 |   ASSERT_EQ(0, fegetexcept()); | 
| Elliott Hughes | 18a1957 | 2017-11-02 16:18:43 -0700 | [diff] [blame] | 198 | #else | 
 | 199 |   // We can't recover from SIGFPE, so sacrifice a child... | 
 | 200 |   pid_t pid = fork(); | 
 | 201 |   ASSERT_NE(-1, pid) << strerror(errno); | 
 | 202 |  | 
 | 203 |   if (pid == 0) { | 
 | 204 |     feclearexcept(FE_ALL_EXCEPT); | 
 | 205 |     ASSERT_EQ(0, fetestexcept(FE_ALL_EXCEPT)); | 
 | 206 |     ASSERT_EQ(0, feenableexcept(FE_INVALID)); | 
 | 207 |     ASSERT_EQ(FE_INVALID, fegetexcept()); | 
 | 208 |     ASSERT_EQ(0, feraiseexcept(FE_INVALID)); | 
 | 209 |     _exit(123); | 
 | 210 |   } | 
 | 211 |  | 
 | 212 |   AssertChildExited(pid, -SIGFPE); | 
 | 213 | #endif | 
 | 214 | } |