Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(46)

Side by Side Diff: base/numerics/safe_math_impl.h

Issue 2472933003: Simplify Checked arithmetic functions in base/numerics (Closed)
Patch Set: first pass Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/numerics/safe_math.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
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, T>::type
136 CheckedAdd(T x, T y, RangeConstraint* validity) { 136 CheckedAdd(T x, T y, bool* validity) {
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 // Addition is valid if the sign of (x + y) is equal to either that of x or 143 // Addition is valid if the sign of (x + y) is equal to either that of x or
144 // that of y. 144 // that of y.
145 if (std::numeric_limits<T>::is_signed) { 145 if (std::numeric_limits<T>::is_signed) {
146 if (HasSignBit(BinaryComplement( 146 *validity = HasSignBit(BinaryComplement(
147 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy))))) { 147 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy))));
148 *validity = RANGE_VALID;
149 } else { // Direction of wrap is inverse of result sign.
150 *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW;
151 }
152 } else { // Unsigned is either valid or overflow. 148 } else { // Unsigned is either valid or overflow.
153 *validity = BinaryComplement(x) >= y ? RANGE_VALID : RANGE_OVERFLOW; 149 *validity = BinaryComplement(x) >= y;
154 } 150 }
155 return static_cast<T>(uresult); 151 return static_cast<T>(uresult);
156 } 152 }
157 153
158 template <typename T> 154 template <typename T>
159 typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type 155 typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type
160 CheckedSub(T x, T y, RangeConstraint* validity) { 156 CheckedSub(T x, T y, bool* validity) {
161 // Since the value of x+y is undefined if we have a signed type, we compute 157 // Since the value of x+y is undefined if we have a signed type, we compute
162 // it using the unsigned type of the same size. 158 // it using the unsigned type of the same size.
163 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; 159 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
164 UnsignedDst ux = static_cast<UnsignedDst>(x); 160 UnsignedDst ux = static_cast<UnsignedDst>(x);
165 UnsignedDst uy = static_cast<UnsignedDst>(y); 161 UnsignedDst uy = static_cast<UnsignedDst>(y);
166 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy); 162 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
167 // 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
168 // the same sign. 164 // the same sign.
169 if (std::numeric_limits<T>::is_signed) { 165 if (std::numeric_limits<T>::is_signed) {
170 if (HasSignBit(BinaryComplement( 166 *validity = HasSignBit(
171 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy))))) { 167 BinaryComplement(static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy))));
172 *validity = RANGE_VALID;
173 } else { // Direction of wrap is inverse of result sign.
174 *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW;
175 }
176 } else { // Unsigned is either valid or underflow. 168 } else { // Unsigned is either valid or underflow.
177 *validity = x >= y ? RANGE_VALID : RANGE_UNDERFLOW; 169 *validity = x >= y;
178 } 170 }
179 return static_cast<T>(uresult); 171 return static_cast<T>(uresult);
180 } 172 }
181 173
182 // Integer multiplication is a bit complicated. In the fast case we just 174 // Integer multiplication is a bit complicated. In the fast case we just
183 // we just promote to a twice wider type, and range check the result. In the 175 // we just promote to a twice wider type, and range check the result. In the
184 // slow case we need to manually check that the result won't be truncated by 176 // slow case we need to manually check that the result won't be truncated by
185 // checking with division against the appropriate bound. 177 // checking with division against the appropriate bound.
186 template <typename T> 178 template <typename T>
187 typename std::enable_if<std::numeric_limits<T>::is_integer && 179 typename std::enable_if<std::numeric_limits<T>::is_integer &&
188 sizeof(T) * 2 <= sizeof(uintmax_t), 180 sizeof(T) * 2 <= sizeof(uintmax_t),
189 T>::type 181 T>::type
190 CheckedMul(T x, T y, RangeConstraint* validity) { 182 CheckedMul(T x, T y, bool* validity) {
191 typedef typename TwiceWiderInteger<T>::type IntermediateType; 183 typedef typename TwiceWiderInteger<T>::type IntermediateType;
192 IntermediateType tmp = 184 IntermediateType tmp =
193 static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y); 185 static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y);
194 *validity = DstRangeRelationToSrcRange<T>(tmp); 186 *validity = DstRangeRelationToSrcRange<T>(tmp) == RANGE_VALID;
195 return static_cast<T>(tmp); 187 return static_cast<T>(tmp);
196 } 188 }
197 189
198 template <typename T> 190 template <typename T>
199 typename std::enable_if<std::numeric_limits<T>::is_integer && 191 typename std::enable_if<std::numeric_limits<T>::is_integer &&
200 std::numeric_limits<T>::is_signed && 192 std::numeric_limits<T>::is_signed &&
201 (sizeof(T) * 2 > sizeof(uintmax_t)), 193 (sizeof(T) * 2 > sizeof(uintmax_t)),
202 T>::type 194 T>::type
203 CheckedMul(T x, T y, RangeConstraint* validity) { 195 CheckedMul(T x, T y, bool* validity) {
204 // If either side is zero then the result will be zero. 196 // If either side is zero then the result will be zero.
205 if (!x || !y) { 197 if (!x || !y) {
206 *validity = RANGE_VALID; 198 *validity = true;
207 return static_cast<T>(0); 199 return static_cast<T>(0);
208 } 200 }
209 if (x > 0) { 201 if (x > 0) {
210 if (y > 0) { 202 if (y > 0) {
211 *validity = 203 *validity = x <= std::numeric_limits<T>::max() / y;
212 x <= std::numeric_limits<T>::max() / y ? RANGE_VALID : RANGE_OVERFLOW;
213 } else { 204 } else {
214 *validity = y >= std::numeric_limits<T>::min() / x ? RANGE_VALID 205 *validity = y >= std::numeric_limits<T>::min() / x;
215 : RANGE_UNDERFLOW;
216 } 206 }
217 } else { 207 } else {
218 if (y > 0) { 208 if (y > 0) {
219 *validity = x >= std::numeric_limits<T>::min() / y ? RANGE_VALID 209 *validity = x >= std::numeric_limits<T>::min() / y;
220 : RANGE_UNDERFLOW;
221 } else { 210 } else {
222 *validity = 211 *validity = y >= std::numeric_limits<T>::max() / x;
223 y >= std::numeric_limits<T>::max() / x ? RANGE_VALID : RANGE_OVERFLOW;
224 } 212 }
225 } 213 }
226 return static_cast<T>(*validity == RANGE_VALID ? x * y : 0); 214 return static_cast<T>(*validity ? x * y : 0);
227 } 215 }
228 216
229 template <typename T> 217 template <typename T>
230 typename std::enable_if<std::numeric_limits<T>::is_integer && 218 typename std::enable_if<std::numeric_limits<T>::is_integer &&
231 !std::numeric_limits<T>::is_signed && 219 !std::numeric_limits<T>::is_signed &&
232 (sizeof(T) * 2 > sizeof(uintmax_t)), 220 (sizeof(T) * 2 > sizeof(uintmax_t)),
233 T>::type 221 T>::type
234 CheckedMul(T x, T y, RangeConstraint* validity) { 222 CheckedMul(T x, T y, bool* validity) {
235 *validity = (y == 0 || x <= std::numeric_limits<T>::max() / y) 223 *validity = (y == 0 || x <= std::numeric_limits<T>::max() / y);
236 ? RANGE_VALID 224 return static_cast<T>(*validity ? x * y : 0);
237 : RANGE_OVERFLOW;
238 return static_cast<T>(*validity == RANGE_VALID ? x * y : 0);
239 } 225 }
240 226
241 // Division just requires a check for a zero denominator or an invalid negation 227 // Division just requires a check for a zero denominator or an invalid negation
242 // on signed min/-1. 228 // on signed min/-1.
243 template <typename T> 229 template <typename T>
244 T CheckedDiv(T x, 230 T CheckedDiv(T x,
245 T y, 231 T y,
246 RangeConstraint* validity, 232 bool* validity,
247 typename std::enable_if<std::numeric_limits<T>::is_integer, 233 typename std::enable_if<std::numeric_limits<T>::is_integer,
248 int>::type = 0) { 234 int>::type = 0) {
249 if (y == 0) { 235 if ((y == 0) ||
250 *validity = RANGE_INVALID; 236 std::numeric_limits<T>::is_signed && x == std::numeric_limits<T>::min() &&
237 y == static_cast<T>(-1)) {
238 *validity = false;
251 return static_cast<T>(0); 239 return static_cast<T>(0);
252 } 240 }
253 if (std::numeric_limits<T>::is_signed && x == std::numeric_limits<T>::min() &&
254 y == static_cast<T>(-1)) {
255 *validity = RANGE_OVERFLOW;
256 return std::numeric_limits<T>::min();
257 }
258 241
259 *validity = RANGE_VALID; 242 *validity = true;
260 return static_cast<T>(x / y); 243 return static_cast<T>(x / y);
261 } 244 }
262 245
263 template <typename T> 246 template <typename T>
264 typename std::enable_if<std::numeric_limits<T>::is_integer && 247 typename std::enable_if<std::numeric_limits<T>::is_integer &&
265 std::numeric_limits<T>::is_signed, 248 std::numeric_limits<T>::is_signed,
266 T>::type 249 T>::type
267 CheckedMod(T x, T y, RangeConstraint* validity) { 250 CheckedMod(T x, T y, bool* validity) {
268 *validity = y > 0 ? RANGE_VALID : RANGE_INVALID; 251 *validity = y > 0;
269 return static_cast<T>(*validity == RANGE_VALID ? x % y: 0); 252 return static_cast<T>(*validity ? x % y : 0);
270 } 253 }
271 254
272 template <typename T> 255 template <typename T>
273 typename std::enable_if<std::numeric_limits<T>::is_integer && 256 typename std::enable_if<std::numeric_limits<T>::is_integer &&
274 !std::numeric_limits<T>::is_signed, 257 !std::numeric_limits<T>::is_signed,
275 T>::type 258 T>::type
276 CheckedMod(T x, T y, RangeConstraint* validity) { 259 CheckedMod(T x, T y, bool* validity) {
277 *validity = y != 0 ? RANGE_VALID : RANGE_INVALID; 260 *validity = y != 0;
278 return static_cast<T>(*validity == RANGE_VALID ? x % y: 0); 261 return static_cast<T>(*validity ? x % y : 0);
279 } 262 }
280 263
281 template <typename T> 264 template <typename T>
282 typename std::enable_if<std::numeric_limits<T>::is_integer && 265 typename std::enable_if<std::numeric_limits<T>::is_integer &&
283 std::numeric_limits<T>::is_signed, 266 std::numeric_limits<T>::is_signed,
284 T>::type 267 T>::type
285 CheckedNeg(T value, RangeConstraint* validity) { 268 CheckedNeg(T value, bool* validity) {
286 *validity = 269 *validity = value != std::numeric_limits<T>::min();
287 value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW;
288 // The negation of signed min is min, so catch that one. 270 // The negation of signed min is min, so catch that one.
289 return static_cast<T>(*validity == RANGE_VALID ? -value : 0); 271 return static_cast<T>(*validity ? -value : 0);
290 } 272 }
291 273
292 template <typename T> 274 template <typename T>
293 typename std::enable_if<std::numeric_limits<T>::is_integer && 275 typename std::enable_if<std::numeric_limits<T>::is_integer &&
294 !std::numeric_limits<T>::is_signed, 276 !std::numeric_limits<T>::is_signed,
295 T>::type 277 T>::type
296 CheckedNeg(T value, RangeConstraint* validity) { 278 CheckedNeg(T value, bool* validity) {
297 // The only legal unsigned negation is zero. 279 // The only legal unsigned negation is zero.
298 *validity = value ? RANGE_UNDERFLOW : RANGE_VALID; 280 *validity = !value;
299 return static_cast<T>(*validity == RANGE_VALID ? 281 return static_cast<T>(
300 -static_cast<typename SignedIntegerForSize<T>::type>(value) : 0); 282 *validity ? -static_cast<typename SignedIntegerForSize<T>::type>(value)
283 : 0);
301 } 284 }
302 285
303 template <typename T> 286 template <typename T>
304 typename std::enable_if<std::numeric_limits<T>::is_integer && 287 typename std::enable_if<std::numeric_limits<T>::is_integer &&
305 std::numeric_limits<T>::is_signed, 288 std::numeric_limits<T>::is_signed,
306 T>::type 289 T>::type
307 CheckedAbs(T value, RangeConstraint* validity) { 290 CheckedAbs(T value, bool* validity) {
308 *validity = 291 *validity = value != std::numeric_limits<T>::min();
309 value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW; 292 return static_cast<T>(*validity ? std::abs(value) : 0);
310 return static_cast<T>(*validity == RANGE_VALID ? std::abs(value) : 0);
311 } 293 }
312 294
313 template <typename T> 295 template <typename T>
314 typename std::enable_if<std::numeric_limits<T>::is_integer && 296 typename std::enable_if<std::numeric_limits<T>::is_integer &&
315 !std::numeric_limits<T>::is_signed, 297 !std::numeric_limits<T>::is_signed,
316 T>::type 298 T>::type
317 CheckedAbs(T value, RangeConstraint* validity) { 299 CheckedAbs(T value, bool* validity) {
318 // T is unsigned, so |value| must already be positive. 300 // T is unsigned, so |value| must already be positive.
319 *validity = RANGE_VALID; 301 *validity = true;
320 return value; 302 return value;
321 } 303 }
322 304
323 template <typename T> 305 template <typename T>
324 typename std::enable_if<std::numeric_limits<T>::is_integer && 306 typename std::enable_if<std::numeric_limits<T>::is_integer &&
325 std::numeric_limits<T>::is_signed, 307 std::numeric_limits<T>::is_signed,
326 typename UnsignedIntegerForSize<T>::type>::type 308 typename UnsignedIntegerForSize<T>::type>::type
327 CheckedUnsignedAbs(T value) { 309 CheckedUnsignedAbs(T value) {
328 typedef typename UnsignedIntegerForSize<T>::type UnsignedT; 310 typedef typename UnsignedIntegerForSize<T>::type UnsignedT;
329 return value == std::numeric_limits<T>::min() 311 return value == std::numeric_limits<T>::min()
330 ? static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1 312 ? static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1
331 : static_cast<UnsignedT>(std::abs(value)); 313 : static_cast<UnsignedT>(std::abs(value));
332 } 314 }
333 315
334 template <typename T> 316 template <typename T>
335 typename std::enable_if<std::numeric_limits<T>::is_integer && 317 typename std::enable_if<std::numeric_limits<T>::is_integer &&
336 !std::numeric_limits<T>::is_signed, 318 !std::numeric_limits<T>::is_signed,
337 T>::type 319 T>::type
338 CheckedUnsignedAbs(T value) { 320 CheckedUnsignedAbs(T value) {
339 // T is unsigned, so |value| must already be positive. 321 // T is unsigned, so |value| must already be positive.
340 return static_cast<T>(value); 322 return static_cast<T>(value);
341 } 323 }
342 324
343 // These are the floating point stubs that the compiler needs to see. Only the 325 // These are the floating point stubs that the compiler needs to see. Only the
344 // negation operation is ever called. 326 // negation operation is ever called.
345 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \ 327 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \
346 template <typename T> \ 328 template <typename T> \
347 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type \ 329 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type \
348 Checked##NAME(T, T, RangeConstraint*) { \ 330 Checked##NAME(T, T, bool*) { \
349 NOTREACHED(); \ 331 NOTREACHED(); \
350 return static_cast<T>(0); \ 332 return static_cast<T>(0); \
351 } 333 }
352 334
353 BASE_FLOAT_ARITHMETIC_STUBS(Add) 335 BASE_FLOAT_ARITHMETIC_STUBS(Add)
354 BASE_FLOAT_ARITHMETIC_STUBS(Sub) 336 BASE_FLOAT_ARITHMETIC_STUBS(Sub)
355 BASE_FLOAT_ARITHMETIC_STUBS(Mul) 337 BASE_FLOAT_ARITHMETIC_STUBS(Mul)
356 BASE_FLOAT_ARITHMETIC_STUBS(Div) 338 BASE_FLOAT_ARITHMETIC_STUBS(Div)
357 BASE_FLOAT_ARITHMETIC_STUBS(Mod) 339 BASE_FLOAT_ARITHMETIC_STUBS(Mod)
358 340
359 #undef BASE_FLOAT_ARITHMETIC_STUBS 341 #undef BASE_FLOAT_ARITHMETIC_STUBS
360 342
361 template <typename T> 343 template <typename T>
362 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg( 344 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg(
363 T value, 345 T value,
364 RangeConstraint*) { 346 bool*) {
365 return static_cast<T>(-value); 347 return static_cast<T>(-value);
366 } 348 }
367 349
368 template <typename T> 350 template <typename T>
369 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs( 351 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs(
370 T value, 352 T value,
371 RangeConstraint*) { 353 bool*) {
372 return static_cast<T>(std::abs(value)); 354 return static_cast<T>(std::abs(value));
373 } 355 }
374 356
375 // Floats carry around their validity state with them, but integers do not. So, 357 // Floats carry around their validity state with them, but integers do not. So,
376 // we wrap the underlying value in a specialization in order to hide that detail 358 // we wrap the underlying value in a specialization in order to hide that detail
377 // and expose an interface via accessors. 359 // and expose an interface via accessors.
378 enum NumericRepresentation { 360 enum NumericRepresentation {
379 NUMERIC_INTEGER, 361 NUMERIC_INTEGER,
380 NUMERIC_FLOATING, 362 NUMERIC_FLOATING,
381 NUMERIC_UNKNOWN 363 NUMERIC_UNKNOWN
(...skipping 10 matching lines...) Expand all
392 374
393 template <typename T, NumericRepresentation type = 375 template <typename T, NumericRepresentation type =
394 GetNumericRepresentation<T>::value> 376 GetNumericRepresentation<T>::value>
395 class CheckedNumericState {}; 377 class CheckedNumericState {};
396 378
397 // Integrals require quite a bit of additional housekeeping to manage state. 379 // Integrals require quite a bit of additional housekeeping to manage state.
398 template <typename T> 380 template <typename T>
399 class CheckedNumericState<T, NUMERIC_INTEGER> { 381 class CheckedNumericState<T, NUMERIC_INTEGER> {
400 private: 382 private:
401 T value_; 383 T value_;
402 RangeConstraint validity_ : CHAR_BIT; // Actually requires only two bits. 384 bool is_valid_;
403 385
404 public: 386 public:
405 template <typename Src, NumericRepresentation type> 387 template <typename Src, NumericRepresentation type>
406 friend class CheckedNumericState; 388 friend class CheckedNumericState;
407 389
408 CheckedNumericState() : value_(0), validity_(RANGE_VALID) {} 390 CheckedNumericState() : value_(0), is_valid_(true) {}
409 391
410 template <typename Src> 392 template <typename Src>
411 CheckedNumericState(Src value, RangeConstraint validity) 393 CheckedNumericState(Src value, bool is_valid)
412 : value_(static_cast<T>(value)), 394 : value_(static_cast<T>(value)),
413 validity_(GetRangeConstraint(validity | 395 is_valid_(is_valid &&
414 DstRangeRelationToSrcRange<T>(value))) { 396 (DstRangeRelationToSrcRange<T>(value) == RANGE_VALID)) {
415 static_assert(std::numeric_limits<Src>::is_specialized, 397 static_assert(std::numeric_limits<Src>::is_specialized,
416 "Argument must be numeric."); 398 "Argument must be numeric.");
417 } 399 }
418 400
419 // Copy constructor. 401 // Copy constructor.
420 template <typename Src> 402 template <typename Src>
421 CheckedNumericState(const CheckedNumericState<Src>& rhs) 403 CheckedNumericState(const CheckedNumericState<Src>& rhs)
422 : value_(static_cast<T>(rhs.value())), 404 : value_(static_cast<T>(rhs.value())), is_valid_(rhs.IsValid()) {}
423 validity_(GetRangeConstraint(
424 rhs.validity() | DstRangeRelationToSrcRange<T>(rhs.value()))) {}
425 405
426 template <typename Src> 406 template <typename Src>
427 explicit CheckedNumericState( 407 explicit CheckedNumericState(
428 Src value, 408 Src value,
429 typename std::enable_if<std::numeric_limits<Src>::is_specialized, 409 typename std::enable_if<std::numeric_limits<Src>::is_specialized,
430 int>::type = 0) 410 int>::type = 0)
431 : value_(static_cast<T>(value)), 411 : value_(static_cast<T>(value)),
432 validity_(DstRangeRelationToSrcRange<T>(value)) {} 412 is_valid_(DstRangeRelationToSrcRange<T>(value) == RANGE_VALID) {}
433 413
434 RangeConstraint validity() const { return validity_; } 414 bool is_valid() const { return is_valid_; }
435 T value() const { return value_; } 415 T value() const { return value_; }
436 }; 416 };
437 417
438 // Floating points maintain their own validity, but need translation wrappers. 418 // Floating points maintain their own validity, but need translation wrappers.
439 template <typename T> 419 template <typename T>
440 class CheckedNumericState<T, NUMERIC_FLOATING> { 420 class CheckedNumericState<T, NUMERIC_FLOATING> {
441 private: 421 private:
442 T value_; 422 T value_;
443 423
444 public: 424 public:
445 template <typename Src, NumericRepresentation type> 425 template <typename Src, NumericRepresentation type>
446 friend class CheckedNumericState; 426 friend class CheckedNumericState;
447 427
448 CheckedNumericState() : value_(0.0) {} 428 CheckedNumericState() : value_(0.0) {}
449 429
450 template <typename Src> 430 template <typename Src>
451 CheckedNumericState( 431 CheckedNumericState(
452 Src value, 432 Src value,
453 RangeConstraint validity, 433 bool is_valid,
454 typename std::enable_if<std::numeric_limits<Src>::is_integer, int>::type = 434 typename std::enable_if<std::numeric_limits<Src>::is_integer, int>::type =
455 0) { 435 0) {
456 switch (DstRangeRelationToSrcRange<T>(value)) { 436 value_ = (is_valid && (DstRangeRelationToSrcRange<T>(value) == RANGE_VALID))
457 case RANGE_VALID: 437 ? value_ = static_cast<T>(value)
458 value_ = static_cast<T>(value); 438 : std::numeric_limits<T>::quiet_NaN();
459 break;
460
461 case RANGE_UNDERFLOW:
462 value_ = -std::numeric_limits<T>::infinity();
463 break;
464
465 case RANGE_OVERFLOW:
466 value_ = std::numeric_limits<T>::infinity();
467 break;
468
469 case RANGE_INVALID:
470 value_ = std::numeric_limits<T>::quiet_NaN();
471 break;
472
473 default:
474 NOTREACHED();
475 }
476 } 439 }
477 440
478 template <typename Src> 441 template <typename Src>
479 explicit CheckedNumericState( 442 explicit CheckedNumericState(
480 Src value, 443 Src value,
481 typename std::enable_if<std::numeric_limits<Src>::is_specialized, 444 typename std::enable_if<std::numeric_limits<Src>::is_specialized,
482 int>::type = 0) 445 int>::type = 0)
483 : value_(static_cast<T>(value)) {} 446 : value_(static_cast<T>(value)) {}
484 447
485 // Copy constructor. 448 // Copy constructor.
486 template <typename Src> 449 template <typename Src>
487 CheckedNumericState(const CheckedNumericState<Src>& rhs) 450 CheckedNumericState(const CheckedNumericState<Src>& rhs)
488 : value_(static_cast<T>(rhs.value())) {} 451 : value_(static_cast<T>(rhs.value())) {}
489 452
490 RangeConstraint validity() const { 453 bool is_valid() const { return isfinite(value_); }
Tom Sepez 2016/11/04 20:46:50 is this std::isfinite() ?
jschuh 2016/11/04 21:41:07 Done.
491 return GetRangeConstraint(value_ <= std::numeric_limits<T>::max(),
492 value_ >= -std::numeric_limits<T>::max());
493 }
494 T value() const { return value_; } 454 T value() const { return value_; }
495 }; 455 };
496 456
497 // For integers less than 128-bit and floats 32-bit or larger, we have the type 457 // For integers less than 128-bit and floats 32-bit or larger, we have the type
498 // with the larger maximum exponent take precedence. 458 // with the larger maximum exponent take precedence.
499 enum ArithmeticPromotionCategory { LEFT_PROMOTION, RIGHT_PROMOTION }; 459 enum ArithmeticPromotionCategory { LEFT_PROMOTION, RIGHT_PROMOTION };
500 460
501 template <typename Lhs, 461 template <typename Lhs,
502 typename Rhs = Lhs, 462 typename Rhs = Lhs,
503 ArithmeticPromotionCategory Promotion = 463 ArithmeticPromotionCategory Promotion =
(...skipping 24 matching lines...) Expand all
528 sizeof(T) >= (2 * sizeof(Lhs)) && 488 sizeof(T) >= (2 * sizeof(Lhs)) &&
529 StaticDstRangeRelationToSrcRange<T, Rhs>::value != 489 StaticDstRangeRelationToSrcRange<T, Rhs>::value !=
530 NUMERIC_RANGE_CONTAINED && 490 NUMERIC_RANGE_CONTAINED &&
531 sizeof(T) >= (2 * sizeof(Rhs)); 491 sizeof(T) >= (2 * sizeof(Rhs));
532 }; 492 };
533 493
534 } // namespace internal 494 } // namespace internal
535 } // namespace base 495 } // namespace base
536 496
537 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ 497 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_
OLDNEW
« no previous file with comments | « base/numerics/safe_math.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698