OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_ | 5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_ |
6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_ | 6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_ |
7 | 7 |
8 #include <stddef.h> | 8 #include <stddef.h> |
9 #include <stdint.h> | 9 #include <stdint.h> |
10 | 10 |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
88 | 88 |
89 template <typename Integer> | 89 template <typename Integer> |
90 struct PositionOfSignBit { | 90 struct PositionOfSignBit { |
91 static const typename std::enable_if<std::numeric_limits<Integer>::is_integer, | 91 static const typename std::enable_if<std::numeric_limits<Integer>::is_integer, |
92 size_t>::type value = | 92 size_t>::type value = |
93 CHAR_BIT * sizeof(Integer) - 1; | 93 CHAR_BIT * sizeof(Integer) - 1; |
94 }; | 94 }; |
95 | 95 |
96 // This is used for UnsignedAbs, where we need to support floating-point | 96 // This is used for UnsignedAbs, where we need to support floating-point |
97 // template instantiations even though we don't actually support the operations. | 97 // template instantiations even though we don't actually support the operations. |
98 // However, there is no corresponding implementation of e.g. CheckedUnsignedAbs, | 98 // However, there is no corresponding implementation of e.g. SafeUnsignedAbs, |
99 // so the float versions will not compile. | 99 // so the float versions will not compile. |
100 template <typename Numeric, | 100 template <typename Numeric, |
101 bool IsInteger = std::numeric_limits<Numeric>::is_integer, | 101 bool IsInteger = std::numeric_limits<Numeric>::is_integer, |
102 bool IsFloat = std::numeric_limits<Numeric>::is_iec559> | 102 bool IsFloat = std::numeric_limits<Numeric>::is_iec559> |
103 struct UnsignedOrFloatForSize; | 103 struct UnsignedOrFloatForSize; |
104 | 104 |
105 template <typename Numeric> | 105 template <typename Numeric> |
106 struct UnsignedOrFloatForSize<Numeric, true, false> { | 106 struct UnsignedOrFloatForSize<Numeric, true, false> { |
107 typedef typename UnsignedIntegerForSize<Numeric>::type type; | 107 typedef typename UnsignedIntegerForSize<Numeric>::type type; |
108 }; | 108 }; |
(...skipping 16 matching lines...) Expand all Loading... |
125 template <typename T> | 125 template <typename T> |
126 constexpr T BinaryComplement(T x) { | 126 constexpr T BinaryComplement(T x) { |
127 return static_cast<T>(~x); | 127 return static_cast<T>(~x); |
128 } | 128 } |
129 | 129 |
130 // Here are the actual portable checked integer math implementations. | 130 // Here are the actual portable checked integer math implementations. |
131 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean | 131 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean |
132 // way to coalesce things into the CheckedNumericState specializations below. | 132 // way to coalesce things into the CheckedNumericState specializations below. |
133 | 133 |
134 template <typename T> | 134 template <typename T> |
135 typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type | 135 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type |
136 CheckedAdd(T x, T y, bool* validity) { | 136 CheckedAdd(T x, T y, T* result) { |
137 // Since the value of x+y is undefined if we have a signed type, we compute | 137 // Since the value of x+y is undefined if we have a signed type, we compute |
138 // it using the unsigned type of the same size. | 138 // it using the unsigned type of the same size. |
139 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; | 139 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; |
140 UnsignedDst ux = static_cast<UnsignedDst>(x); | 140 UnsignedDst ux = static_cast<UnsignedDst>(x); |
141 UnsignedDst uy = static_cast<UnsignedDst>(y); | 141 UnsignedDst uy = static_cast<UnsignedDst>(y); |
142 UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy); | 142 UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy); |
| 143 *result = static_cast<T>(uresult); |
143 // Addition is valid if the sign of (x + y) is equal to either that of x or | 144 // Addition is valid if the sign of (x + y) is equal to either that of x or |
144 // that of y. | 145 // that of y. |
145 if (std::numeric_limits<T>::is_signed) { | 146 return (std::numeric_limits<T>::is_signed) |
146 *validity = HasSignBit(BinaryComplement( | 147 ? HasSignBit(BinaryComplement( |
147 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy)))); | 148 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy)))) |
148 } else { // Unsigned is either valid or overflow. | 149 : (BinaryComplement(x) >= |
149 *validity = BinaryComplement(x) >= y; | 150 y); // Unsigned is either valid or underflow. |
150 } | |
151 return static_cast<T>(uresult); | |
152 } | 151 } |
153 | 152 |
154 template <typename T> | 153 template <typename T> |
155 typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type | 154 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type |
156 CheckedSub(T x, T y, bool* validity) { | 155 CheckedSub(T x, T y, T* result) { |
157 // Since the value of x+y is undefined if we have a signed type, we compute | 156 // Since the value of x+y is undefined if we have a signed type, we compute |
158 // it using the unsigned type of the same size. | 157 // it using the unsigned type of the same size. |
159 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; | 158 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; |
160 UnsignedDst ux = static_cast<UnsignedDst>(x); | 159 UnsignedDst ux = static_cast<UnsignedDst>(x); |
161 UnsignedDst uy = static_cast<UnsignedDst>(y); | 160 UnsignedDst uy = static_cast<UnsignedDst>(y); |
162 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy); | 161 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy); |
| 162 *result = static_cast<T>(uresult); |
163 // Subtraction is valid if either x and y have same sign, or (x-y) and x have | 163 // Subtraction is valid if either x and y have same sign, or (x-y) and x have |
164 // the same sign. | 164 // the same sign. |
165 if (std::numeric_limits<T>::is_signed) { | 165 return (std::numeric_limits<T>::is_signed) |
166 *validity = HasSignBit( | 166 ? HasSignBit(BinaryComplement( |
167 BinaryComplement(static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy)))); | 167 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy)))) |
168 } else { // Unsigned is either valid or underflow. | 168 : (x >= y); |
169 *validity = x >= y; | |
170 } | |
171 return static_cast<T>(uresult); | |
172 } | 169 } |
173 | 170 |
174 // Integer multiplication is a bit complicated. In the fast case we just | 171 // Integer multiplication is a bit complicated. In the fast case we just |
175 // we just promote to a twice wider type, and range check the result. In the | 172 // we just promote to a twice wider type, and range check the result. In the |
176 // slow case we need to manually check that the result won't be truncated by | 173 // slow case we need to manually check that the result won't be truncated by |
177 // checking with division against the appropriate bound. | 174 // checking with division against the appropriate bound. |
178 template <typename T> | 175 template <typename T> |
179 typename std::enable_if<std::numeric_limits<T>::is_integer && | 176 typename std::enable_if<std::numeric_limits<T>::is_integer && |
180 sizeof(T) * 2 <= sizeof(uintmax_t), | 177 sizeof(T) * 2 <= sizeof(uintmax_t), |
181 T>::type | 178 bool>::type |
182 CheckedMul(T x, T y, bool* validity) { | 179 CheckedMul(T x, T y, T* result) { |
183 typedef typename TwiceWiderInteger<T>::type IntermediateType; | 180 typedef typename TwiceWiderInteger<T>::type IntermediateType; |
184 IntermediateType tmp = | 181 IntermediateType tmp = |
185 static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y); | 182 static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y); |
186 *validity = DstRangeRelationToSrcRange<T>(tmp) == RANGE_VALID; | 183 *result = static_cast<T>(tmp); |
187 return static_cast<T>(tmp); | 184 return DstRangeRelationToSrcRange<T>(tmp) == RANGE_VALID; |
188 } | 185 } |
189 | 186 |
190 template <typename T> | 187 template <typename T> |
191 typename std::enable_if<std::numeric_limits<T>::is_integer && | 188 typename std::enable_if<std::numeric_limits<T>::is_integer && |
192 std::numeric_limits<T>::is_signed && | 189 std::numeric_limits<T>::is_signed && |
193 (sizeof(T) * 2 > sizeof(uintmax_t)), | 190 (sizeof(T) * 2 > sizeof(uintmax_t)), |
194 T>::type | 191 bool>::type |
195 CheckedMul(T x, T y, bool* validity) { | 192 CheckedMul(T x, T y, T* result) { |
196 // If either side is zero then the result will be zero. | 193 if (x && y) { |
197 if (!x || !y) { | 194 if (x > 0) { |
198 *validity = true; | 195 if (y > 0) { |
199 return static_cast<T>(0); | 196 if (x > std::numeric_limits<T>::max() / y) |
200 } | 197 return false; |
201 if (x > 0) { | 198 } else { |
202 if (y > 0) { | 199 if (y < std::numeric_limits<T>::min() / x) |
203 *validity = x <= std::numeric_limits<T>::max() / y; | 200 return false; |
| 201 } |
204 } else { | 202 } else { |
205 *validity = y >= std::numeric_limits<T>::min() / x; | 203 if (y > 0) { |
206 } | 204 if (x < std::numeric_limits<T>::min() / y) |
207 } else { | 205 return false; |
208 if (y > 0) { | 206 } else { |
209 *validity = x >= std::numeric_limits<T>::min() / y; | 207 if (y < std::numeric_limits<T>::max() / x) |
210 } else { | 208 return false; |
211 *validity = y >= std::numeric_limits<T>::max() / x; | 209 } |
212 } | 210 } |
213 } | 211 } |
214 return static_cast<T>(*validity ? x * y : 0); | 212 *result = x * y; |
| 213 return true; |
215 } | 214 } |
216 | 215 |
217 template <typename T> | 216 template <typename T> |
218 typename std::enable_if<std::numeric_limits<T>::is_integer && | 217 typename std::enable_if<std::numeric_limits<T>::is_integer && |
219 !std::numeric_limits<T>::is_signed && | 218 !std::numeric_limits<T>::is_signed && |
220 (sizeof(T) * 2 > sizeof(uintmax_t)), | 219 (sizeof(T) * 2 > sizeof(uintmax_t)), |
221 T>::type | 220 bool>::type |
222 CheckedMul(T x, T y, bool* validity) { | 221 CheckedMul(T x, T y, T* result) { |
223 *validity = (y == 0 || x <= std::numeric_limits<T>::max() / y); | 222 *result = x * y; |
224 return static_cast<T>(*validity ? x * y : 0); | 223 return (y == 0 || x <= std::numeric_limits<T>::max() / y); |
225 } | 224 } |
226 | 225 |
227 // Division just requires a check for a zero denominator or an invalid negation | 226 // Division just requires a check for a zero denominator or an invalid negation |
228 // on signed min/-1. | 227 // on signed min/-1. |
229 template <typename T> | 228 template <typename T> |
230 T CheckedDiv(T x, | 229 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type |
231 T y, | 230 CheckedDiv(T x, T y, T* result) { |
232 bool* validity, | 231 if (y && (!std::numeric_limits<T>::is_signed || |
233 typename std::enable_if<std::numeric_limits<T>::is_integer, | 232 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) { |
234 int>::type = 0) { | 233 *result = x / y; |
235 if ((y == 0) || | 234 return true; |
236 (std::numeric_limits<T>::is_signed && | |
237 x == std::numeric_limits<T>::min() && y == static_cast<T>(-1))) { | |
238 *validity = false; | |
239 return static_cast<T>(0); | |
240 } | 235 } |
241 | 236 return false; |
242 *validity = true; | |
243 return static_cast<T>(x / y); | |
244 } | 237 } |
245 | 238 |
246 template <typename T> | 239 template <typename T> |
247 typename std::enable_if<std::numeric_limits<T>::is_integer && | 240 typename std::enable_if<std::numeric_limits<T>::is_integer && |
248 std::numeric_limits<T>::is_signed, | 241 std::numeric_limits<T>::is_signed, |
249 T>::type | 242 bool>::type |
250 CheckedMod(T x, T y, bool* validity) { | 243 CheckedMod(T x, T y, T* result) { |
251 *validity = y > 0; | 244 if (y > 0) { |
252 return static_cast<T>(*validity ? x % y : 0); | 245 *result = static_cast<T>(x % y); |
| 246 return true; |
| 247 } |
| 248 return false; |
253 } | 249 } |
254 | 250 |
255 template <typename T> | 251 template <typename T> |
256 typename std::enable_if<std::numeric_limits<T>::is_integer && | 252 typename std::enable_if<std::numeric_limits<T>::is_integer && |
257 !std::numeric_limits<T>::is_signed, | 253 !std::numeric_limits<T>::is_signed, |
258 T>::type | 254 bool>::type |
259 CheckedMod(T x, T y, bool* validity) { | 255 CheckedMod(T x, T y, T* result) { |
260 *validity = y != 0; | 256 if (y != 0) { |
261 return static_cast<T>(*validity ? x % y : 0); | 257 *result = static_cast<T>(x % y); |
| 258 return true; |
| 259 } |
| 260 return false; |
262 } | 261 } |
263 | 262 |
264 template <typename T> | 263 template <typename T> |
265 typename std::enable_if<std::numeric_limits<T>::is_integer && | 264 typename std::enable_if<std::numeric_limits<T>::is_integer && |
266 std::numeric_limits<T>::is_signed, | 265 std::numeric_limits<T>::is_signed, |
267 T>::type | 266 bool>::type |
268 CheckedNeg(T value, bool* validity) { | 267 CheckedNeg(T value, T* result) { |
269 *validity = value != std::numeric_limits<T>::min(); | |
270 // The negation of signed min is min, so catch that one. | 268 // The negation of signed min is min, so catch that one. |
271 return static_cast<T>(*validity ? -value : 0); | 269 if (value != std::numeric_limits<T>::min()) { |
| 270 *result = static_cast<T>(-value); |
| 271 return true; |
| 272 } |
| 273 return false; |
272 } | 274 } |
273 | 275 |
274 template <typename T> | 276 template <typename T> |
275 typename std::enable_if<std::numeric_limits<T>::is_integer && | 277 typename std::enable_if<std::numeric_limits<T>::is_integer && |
276 !std::numeric_limits<T>::is_signed, | 278 !std::numeric_limits<T>::is_signed, |
277 T>::type | 279 bool>::type |
278 CheckedNeg(T value, bool* validity) { | 280 CheckedNeg(T value, T* result) { |
279 // The only legal unsigned negation is zero. | 281 if (!value) { // The only legal unsigned negation is zero. |
280 *validity = !value; | 282 *result = static_cast<T>(0); |
281 return static_cast<T>( | 283 return true; |
282 *validity ? -static_cast<typename SignedIntegerForSize<T>::type>(value) | 284 } |
283 : 0); | 285 return false; |
284 } | 286 } |
285 | 287 |
286 template <typename T> | 288 template <typename T> |
287 typename std::enable_if<std::numeric_limits<T>::is_integer && | 289 typename std::enable_if<std::numeric_limits<T>::is_integer && |
288 std::numeric_limits<T>::is_signed, | 290 std::numeric_limits<T>::is_signed, |
289 T>::type | 291 bool>::type |
290 CheckedAbs(T value, bool* validity) { | 292 CheckedAbs(T value, T* result) { |
291 *validity = value != std::numeric_limits<T>::min(); | 293 if (value != std::numeric_limits<T>::min()) { |
292 return static_cast<T>(*validity ? std::abs(value) : 0); | 294 *result = std::abs(value); |
| 295 return true; |
| 296 } |
| 297 return false; |
293 } | 298 } |
294 | 299 |
295 template <typename T> | 300 template <typename T> |
296 typename std::enable_if<std::numeric_limits<T>::is_integer && | 301 typename std::enable_if<std::numeric_limits<T>::is_integer && |
297 !std::numeric_limits<T>::is_signed, | 302 !std::numeric_limits<T>::is_signed, |
298 T>::type | 303 bool>::type |
299 CheckedAbs(T value, bool* validity) { | 304 CheckedAbs(T value, T* result) { |
300 // T is unsigned, so |value| must already be positive. | 305 // T is unsigned, so |value| must already be positive. |
301 *validity = true; | 306 *result = value; |
302 return value; | 307 return true; |
303 } | 308 } |
304 | 309 |
305 template <typename T> | 310 template <typename T> |
306 typename std::enable_if<std::numeric_limits<T>::is_integer && | 311 typename std::enable_if<std::numeric_limits<T>::is_integer && |
307 std::numeric_limits<T>::is_signed, | 312 std::numeric_limits<T>::is_signed, |
308 typename UnsignedIntegerForSize<T>::type>::type | 313 typename UnsignedIntegerForSize<T>::type>::type |
309 CheckedUnsignedAbs(T value) { | 314 SafeUnsignedAbs(T value) { |
310 typedef typename UnsignedIntegerForSize<T>::type UnsignedT; | 315 typedef typename UnsignedIntegerForSize<T>::type UnsignedT; |
311 return value == std::numeric_limits<T>::min() | 316 return value == std::numeric_limits<T>::min() |
312 ? static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1 | 317 ? static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1 |
313 : static_cast<UnsignedT>(std::abs(value)); | 318 : static_cast<UnsignedT>(std::abs(value)); |
314 } | 319 } |
315 | 320 |
316 template <typename T> | 321 template <typename T> |
317 typename std::enable_if<std::numeric_limits<T>::is_integer && | 322 typename std::enable_if<std::numeric_limits<T>::is_integer && |
318 !std::numeric_limits<T>::is_signed, | 323 !std::numeric_limits<T>::is_signed, |
319 T>::type | 324 T>::type |
320 CheckedUnsignedAbs(T value) { | 325 SafeUnsignedAbs(T value) { |
321 // T is unsigned, so |value| must already be positive. | 326 // T is unsigned, so |value| must already be positive. |
322 return static_cast<T>(value); | 327 return static_cast<T>(value); |
323 } | 328 } |
324 | 329 |
325 // These are the floating point stubs that the compiler needs to see. Only the | 330 template <typename T> |
326 // negation operation is ever called. | 331 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg( |
327 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \ | 332 T value, |
328 template <typename T> \ | 333 bool*) { |
329 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type \ | 334 NOTREACHED(); |
330 Checked##NAME(T, T, bool*) { \ | 335 return static_cast<T>(-value); |
331 NOTREACHED(); \ | 336 } |
332 return static_cast<T>(0); \ | 337 |
| 338 template <typename T> |
| 339 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs( |
| 340 T value, |
| 341 bool*) { |
| 342 NOTREACHED(); |
| 343 return static_cast<T>(std::abs(value)); |
| 344 } |
| 345 |
| 346 // These are the floating point stubs that the compiler needs to see. |
| 347 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \ |
| 348 template <typename T> \ |
| 349 typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type \ |
| 350 Checked##NAME(T, T, T*) { \ |
| 351 NOTREACHED(); \ |
| 352 return static_cast<T>(false); \ |
333 } | 353 } |
334 | 354 |
335 BASE_FLOAT_ARITHMETIC_STUBS(Add) | 355 BASE_FLOAT_ARITHMETIC_STUBS(Add) |
336 BASE_FLOAT_ARITHMETIC_STUBS(Sub) | 356 BASE_FLOAT_ARITHMETIC_STUBS(Sub) |
337 BASE_FLOAT_ARITHMETIC_STUBS(Mul) | 357 BASE_FLOAT_ARITHMETIC_STUBS(Mul) |
338 BASE_FLOAT_ARITHMETIC_STUBS(Div) | 358 BASE_FLOAT_ARITHMETIC_STUBS(Div) |
339 BASE_FLOAT_ARITHMETIC_STUBS(Mod) | 359 BASE_FLOAT_ARITHMETIC_STUBS(Mod) |
340 | 360 |
341 #undef BASE_FLOAT_ARITHMETIC_STUBS | 361 #undef BASE_FLOAT_ARITHMETIC_STUBS |
342 | 362 |
343 template <typename T> | 363 template <typename T> |
344 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg( | 364 typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type |
345 T value, | 365 CheckedNeg(T value, T* result) { |
346 bool*) { | 366 *result = static_cast<T>(-value); |
347 return static_cast<T>(-value); | 367 return true; |
348 } | 368 } |
349 | 369 |
350 template <typename T> | 370 template <typename T> |
351 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs( | 371 typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type |
352 T value, | 372 CheckedAbs(T value, T* result) { |
353 bool*) { | 373 *result = static_cast<T>(std::abs(value)); |
354 return static_cast<T>(std::abs(value)); | 374 return true; |
355 } | 375 } |
356 | 376 |
357 // Floats carry around their validity state with them, but integers do not. So, | 377 // Floats carry around their validity state with them, but integers do not. So, |
358 // we wrap the underlying value in a specialization in order to hide that detail | 378 // we wrap the underlying value in a specialization in order to hide that detail |
359 // and expose an interface via accessors. | 379 // and expose an interface via accessors. |
360 enum NumericRepresentation { | 380 enum NumericRepresentation { |
361 NUMERIC_INTEGER, | 381 NUMERIC_INTEGER, |
362 NUMERIC_FLOATING, | 382 NUMERIC_FLOATING, |
363 NUMERIC_UNKNOWN | 383 NUMERIC_UNKNOWN |
364 }; | 384 }; |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
488 sizeof(T) >= (2 * sizeof(Lhs)) && | 508 sizeof(T) >= (2 * sizeof(Lhs)) && |
489 StaticDstRangeRelationToSrcRange<T, Rhs>::value != | 509 StaticDstRangeRelationToSrcRange<T, Rhs>::value != |
490 NUMERIC_RANGE_CONTAINED && | 510 NUMERIC_RANGE_CONTAINED && |
491 sizeof(T) >= (2 * sizeof(Rhs)); | 511 sizeof(T) >= (2 * sizeof(Rhs)); |
492 }; | 512 }; |
493 | 513 |
494 } // namespace internal | 514 } // namespace internal |
495 } // namespace base | 515 } // namespace base |
496 | 516 |
497 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ | 517 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ |
OLD | NEW |