aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/common/qjsnumbercoercion.h
blob: 2e1e637eafbc77128b9682279e8300f8e92fee57 (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
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant

#ifndef QJSNUMBERCOERCION_H
#define QJSNUMBERCOERCION_H

#include <QtCore/qglobal.h>
#include <cstring>

QT_BEGIN_NAMESPACE

class QJSNumberCoercion
{
public:

    static constexpr bool isInteger(double d)
    {
        // Comparing d with itself checks for NaN and comparing d with the min and max values
        // for int also covers infinities.
        if (!equals(d, d) || d < (std::numeric_limits<int>::min)()
            || d > (std::numeric_limits<int>::max)()) {
            return false;
        }

        return equals(static_cast<int>(d), d);
    }

    static constexpr bool isArrayIndex(double d)
    {
        return d >= 0
                && equals(d, d)
                && d <= (std::numeric_limits<uint>::max)()
                && equals(static_cast<uint>(d), d);
    }

    static constexpr bool isArrayIndex(qint64 i)
    {
        return i >= 0 && i <= (std::numeric_limits<uint>::max)();
    }

    static constexpr bool isArrayIndex(quint64 i)
    {
        return i <= (std::numeric_limits<uint>::max)();
    }

    static constexpr int toInteger(double d) {
        // Check for NaN
        if (!equals(d, d))
            return 0;

        if (d >= (std::numeric_limits<int>::min)() && d <= (std::numeric_limits<int>::max)()) {
            const int i = static_cast<int>(d);
            if (equals(i, d))
                return i;
        }

        return QJSNumberCoercion(d).toInteger();
    }

    static constexpr bool equals(double lhs, double rhs)
    {
        QT_WARNING_PUSH
        QT_WARNING_DISABLE_FLOAT_COMPARE
        return lhs == rhs;
        QT_WARNING_POP
    }

    static constexpr double roundTowards0(double d)
    {
        // Check for NaN
        if (!equals(d, d))
            return +0;

        if (equals(d, 0) || std::isinf(d))
            return d;

        return d >= 0 ? std::floor(d) : std::ceil(d);
    }

private:
    constexpr QJSNumberCoercion(double dbl)
    {
        // the dbl == 0 path is guaranteed constexpr. The other one may or may not be, depending
        // on whether and how the compiler inlines the memcpy.
        // In order to declare the ctor constexpr we need one guaranteed constexpr path.
        if (!equals(dbl, 0))
            memcpy(&d, &dbl, sizeof(double));
    }

    constexpr int sign() const
    {
        return (d >> 63) ? -1 : 1;
    }

    constexpr bool isDenormal() const
    {
        return static_cast<int>((d << 1) >> 53) == 0;
    }

    constexpr int exponent() const
    {
        return static_cast<int>((d << 1) >> 53) - 1023;
    }

    constexpr quint64 significant() const
    {
        quint64 m = (d << 12) >> 12;
        if (!isDenormal())
            m |= (static_cast<quint64>(1) << 52);
        return m;
    }

    constexpr int toInteger()
    {
        int e = exponent() - 52;
        if (e < 0) {
            if (e <= -53)
                return 0;
            return sign() * static_cast<int>(significant() >> -e);
        } else {
            if (e > 31)
                return 0;
            return sign() * (static_cast<int>(significant()) << e);
        }
    }

    quint64 d = 0;
};

QT_END_NAMESPACE

#endif // QJSNUMBERCOERCION_H