summaryrefslogtreecommitdiff
path: root/numpy/core/src/common/float_status.hpp
blob: 8e4d5e06a59c4f4f77a99880e62a1d039e50f8b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#ifndef NUMPY_CORE_SRC_COMMON_FLOAT_STATUS_HPP
#define NUMPY_CORE_SRC_COMMON_FLOAT_STATUS_HPP

#include "npstd.hpp"

#include <fenv.h>

namespace np {

/// @addtogroup cpp_core_utility
/// @{
/**
 * Class wraps floating-point environment operations,
 * provides lazy access to its functionality.
 */
class FloatStatus {
 public:
/*
 * According to the C99 standard FE_DIVBYZERO, etc. may not be provided when
 * unsupported.  In such cases NumPy will not report these correctly, but we
 * should still allow compiling (whether tests pass or not).
 * By defining them as 0 locally, we make them no-ops.  Unlike these defines,
 * for example `musl` still defines all of the functions (as no-ops):
 *     https://git.musl-libc.org/cgit/musl/tree/src/fenv/fenv.c
 * and does similar replacement in its tests:
 * http://nsz.repo.hu/git/?p=libc-test;a=blob;f=src/common/mtest.h;h=706c1ba23ea8989b17a2f72ed1a919e187c06b6a;hb=HEAD#l30
 */
#ifdef FE_DIVBYZERO
    static constexpr int kDivideByZero = FE_DIVBYZERO;
#else
    static constexpr int kDivideByZero = 0;
#endif
#ifdef FE_INVALID
    static constexpr int kInvalid = FE_INVALID;
#else
    static constexpr int kInvalid = 0;
#endif
#ifdef FE_INEXACT
    static constexpr int kInexact = FE_INEXACT;
#else
    static constexpr int kInexact = 0;
#endif
#ifdef FE_OVERFLOW
    static constexpr int kOverflow = FE_OVERFLOW;
#else
    static constexpr int kOverflow = 0;
#endif
#ifdef FE_UNDERFLOW
    static constexpr int kUnderflow = FE_UNDERFLOW;
#else
    static constexpr int kUnderflow = 0;
#endif
    static constexpr int kAllExcept = (kDivideByZero | kInvalid | kInexact |
                                       kOverflow | kUnderflow);

    FloatStatus(bool clear_on_dst=true)
        : clear_on_dst_(clear_on_dst)
    {
        if constexpr (kAllExcept != 0) {
            fpstatus_ = fetestexcept(kAllExcept);
        }
        else {
            fpstatus_ = 0;
        }
    }
    ~FloatStatus()
    {
        if constexpr (kAllExcept != 0) {
            if (fpstatus_ != 0 && clear_on_dst_) {
                feclearexcept(kAllExcept);
            }
        }
    }
    constexpr bool IsDivideByZero() const
    {
        return (fpstatus_ & kDivideByZero) != 0;
    }
    constexpr bool IsInexact() const
    {
        return (fpstatus_ & kInexact) != 0;
    }
    constexpr bool IsInvalid() const
    {
        return (fpstatus_ & kInvalid) != 0;
    }
    constexpr bool IsOverFlow() const
    {
        return (fpstatus_ & kOverflow) != 0;
    }
    constexpr bool IsUnderFlow() const
    {
        return (fpstatus_ & kUnderflow) != 0;
    }
    static void RaiseDivideByZero()
    {
        if constexpr (kDivideByZero != 0) {
            feraiseexcept(kDivideByZero);
        }
    }
    static void RaiseInexact()
    {
        if constexpr (kInexact != 0) {
            feraiseexcept(kInexact);
        }
    }
    static void RaiseInvalid()
    {
        if constexpr (kInvalid != 0) {
            feraiseexcept(kInvalid);
        }
    }
    static void RaiseOverflow()
    {
        if constexpr (kOverflow != 0) {
            feraiseexcept(kOverflow);
        }
    }
    static void RaiseUnderflow()
    {
        if constexpr (kUnderflow != 0) {
            feraiseexcept(kUnderflow);
        }
    }

  private:
    bool clear_on_dst_;
    int fpstatus_;
};

/// @} cpp_core_utility
} // namespace np

#endif // NUMPY_CORE_SRC_COMMON_FLOAT_STATUS_HPP