1/*
2 * Copyright 2011 Google Inc.
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 "SkFloatingPoint.h"
9#include "SkMath.h"
10#include "SkPoint.h"
11#include "SkRandom.h"
12#include "SkRect.h"
13#include "Test.h"
14
15static void test_roundtoint(skiatest::Reporter* reporter) {
16    SkScalar x = 0.49999997f;
17    int ix = SkScalarRoundToInt(x);
18    // We "should" get 0, since x < 0.5, but we don't due to float addition rounding up the low
19    // bit after adding 0.5.
20    REPORTER_ASSERT(reporter, 1 == ix);
21
22    // This version explicitly performs the +0.5 step using double, which should avoid losing the
23    // low bits.
24    ix = SkDScalarRoundToInt(x);
25    REPORTER_ASSERT(reporter, 0 == ix);
26}
27
28struct PointSet {
29    const SkPoint* fPts;
30    size_t         fCount;
31    bool           fIsFinite;
32};
33
34static void test_isRectFinite(skiatest::Reporter* reporter) {
35    static const SkPoint gF0[] = {
36        { 0, 0 }, { 1, 1 }
37    };
38    static const SkPoint gF1[] = {
39        { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }
40    };
41
42    static const SkPoint gI0[] = {
43        { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { SK_ScalarNaN, 3 }, { 2, 3 },
44    };
45    static const SkPoint gI1[] = {
46        { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { 3, SK_ScalarNaN }, { 2, 3 },
47    };
48    static const SkPoint gI2[] = {
49        { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { SK_ScalarInfinity, 3 }, { 2, 3 },
50    };
51    static const SkPoint gI3[] = {
52        { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { 3, SK_ScalarInfinity }, { 2, 3 },
53    };
54
55    static const struct {
56        const SkPoint* fPts;
57        size_t         fCount;
58        bool           fIsFinite;
59    } gSets[] = {
60        { gF0, SK_ARRAY_COUNT(gF0), true },
61        { gF1, SK_ARRAY_COUNT(gF1), true },
62
63        { gI0, SK_ARRAY_COUNT(gI0), false },
64        { gI1, SK_ARRAY_COUNT(gI1), false },
65        { gI2, SK_ARRAY_COUNT(gI2), false },
66        { gI3, SK_ARRAY_COUNT(gI3), false },
67    };
68
69    for (size_t i = 0; i < SK_ARRAY_COUNT(gSets); ++i) {
70        SkRect r;
71        r.set(gSets[i].fPts, gSets[i].fCount);
72        bool rectIsFinite = !r.isEmpty();
73        REPORTER_ASSERT(reporter, gSets[i].fIsFinite == rectIsFinite);
74    }
75}
76
77static bool isFinite_int(float x) {
78    uint32_t bits = SkFloat2Bits(x);    // need unsigned for our shifts
79    int exponent = bits << 1 >> 24;
80    return exponent != 0xFF;
81}
82
83static bool isFinite_float(float x) {
84    return SkToBool(sk_float_isfinite(x));
85}
86
87static bool isFinite_mulzero(float x) {
88    float y = x * 0;
89    return y == y;
90}
91
92// return true if the float is finite
93typedef bool (*IsFiniteProc1)(float);
94
95static bool isFinite2_and(float x, float y, IsFiniteProc1 proc) {
96    return proc(x) && proc(y);
97}
98
99static bool isFinite2_mulzeroadd(float x, float y, IsFiniteProc1 proc) {
100    return proc(x * 0 + y * 0);
101}
102
103// return true if both floats are finite
104typedef bool (*IsFiniteProc2)(float, float, IsFiniteProc1);
105
106enum FloatClass {
107    kFinite,
108    kInfinite,
109    kNaN
110};
111
112static void test_floatclass(skiatest::Reporter* reporter, float value, FloatClass fc) {
113    // our sk_float_is... function may return int instead of bool,
114    // hence the double ! to turn it into a bool
115    REPORTER_ASSERT(reporter, !!sk_float_isfinite(value) == (fc == kFinite));
116    REPORTER_ASSERT(reporter, !!sk_float_isinf(value) == (fc == kInfinite));
117    REPORTER_ASSERT(reporter, !!sk_float_isnan(value) == (fc == kNaN));
118}
119
120#if defined _WIN32
121#pragma warning ( push )
122// we are intentionally causing an overflow here
123//      (warning C4756: overflow in constant arithmetic)
124#pragma warning ( disable : 4756 )
125#endif
126
127static void test_isfinite(skiatest::Reporter* reporter) {
128    struct Rec {
129        float   fValue;
130        bool    fIsFinite;
131    };
132
133    float max = 3.402823466e+38f;
134    float inf = max * max;
135    float nan = inf * 0;
136
137    test_floatclass(reporter,    0, kFinite);
138    test_floatclass(reporter,  max, kFinite);
139    test_floatclass(reporter, -max, kFinite);
140    test_floatclass(reporter,  inf, kInfinite);
141    test_floatclass(reporter, -inf, kInfinite);
142    test_floatclass(reporter,  nan, kNaN);
143    test_floatclass(reporter, -nan, kNaN);
144
145    const Rec data[] = {
146        {   0,           true    },
147        {   1,           true    },
148        {  -1,           true    },
149        {  max * 0.75f,  true    },
150        {  max,          true    },
151        {  -max * 0.75f, true    },
152        {  -max,         true    },
153        {  inf,          false   },
154        { -inf,          false   },
155        {  nan,          false   },
156    };
157
158    const IsFiniteProc1 gProc1[] = {
159        isFinite_int,
160        isFinite_float,
161        isFinite_mulzero
162    };
163    const IsFiniteProc2 gProc2[] = {
164        isFinite2_and,
165        isFinite2_mulzeroadd
166    };
167
168    size_t i, n = SK_ARRAY_COUNT(data);
169
170    for (i = 0; i < n; ++i) {
171        for (size_t k = 0; k < SK_ARRAY_COUNT(gProc1); ++k) {
172            const Rec& rec = data[i];
173            bool finite = gProc1[k](rec.fValue);
174            REPORTER_ASSERT(reporter, rec.fIsFinite == finite);
175        }
176    }
177
178    for (i = 0; i < n; ++i) {
179        const Rec& rec0 = data[i];
180        for (size_t j = 0; j < n; ++j) {
181            const Rec& rec1 = data[j];
182            for (size_t k = 0; k < SK_ARRAY_COUNT(gProc1); ++k) {
183                IsFiniteProc1 proc1 = gProc1[k];
184
185                for (size_t m = 0; m < SK_ARRAY_COUNT(gProc2); ++m) {
186                    bool finite = gProc2[m](rec0.fValue, rec1.fValue, proc1);
187                    bool finite2 = rec0.fIsFinite && rec1.fIsFinite;
188                    REPORTER_ASSERT(reporter, finite2 == finite);
189                }
190            }
191        }
192    }
193
194    test_isRectFinite(reporter);
195}
196
197#if defined _WIN32
198#pragma warning ( pop )
199#endif
200
201DEF_TEST(Scalar, reporter) {
202    test_isfinite(reporter);
203    test_roundtoint(reporter);
204}
205