• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/private/base/SkFloatingPoint.h"
9 
10 #include "include/private/base/SkAssert.h"
11 
12 #include <cmath>
13 
double_to_twos_complement_bits(double x)14 static inline int64_t double_to_twos_complement_bits(double x) {
15     // Convert a double to its bit pattern
16     int64_t bits = 0;
17     static_assert(sizeof(x) == sizeof(bits));
18     std::memcpy(&bits, &x, sizeof(bits));
19     // Convert a sign-bit int (i.e. double interpreted as int) into a 2s complement
20     // int. This also converts -0 (0x8000000000000000) to 0. Doing this to a double allows
21     // it to be compared using normal C operators (<, <=, etc.)
22     if (bits < 0) {
23         bits &= 0x7FFFFFFFFFFFFFFF;
24         bits = -bits;
25     }
26     return bits;
27 }
28 
29 // Arbitrarily chosen.
30 constexpr static double sk_double_epsilon = 0.0000000001;
31 
sk_doubles_nearly_equal_ulps(double a,double b,uint8_t max_ulps_diff)32 bool sk_doubles_nearly_equal_ulps(double a, double b, uint8_t max_ulps_diff) {
33     // If both of these are zero (or very close), then using Units of Least Precision
34     // will not be accurate and we should use sk_double_nearly_zero instead.
35     SkASSERT(!(fabs(a) < sk_double_epsilon && fabs(b) < sk_double_epsilon));
36     // This algorithm does not work if both inputs are NaN.
37     SkASSERT(!(std::isnan(a) && std::isnan(b)));
38     // If both inputs are infinity (or actually equal), this catches it.
39     if (a == b) {
40         return true;
41     }
42     int64_t aBits = double_to_twos_complement_bits(a);
43     int64_t bBits = double_to_twos_complement_bits(b);
44 
45     // Find the difference in Units of Least Precision (ULPs).
46     return aBits < bBits + max_ulps_diff && bBits < aBits + max_ulps_diff;
47 }
48 
sk_double_nearly_zero(double a)49 bool sk_double_nearly_zero(double a) {
50     return a == 0 || fabs(a) < sk_double_epsilon;
51 }
52