1 /*
2  * PROJECT:     ReactOS API tests
3  * LICENSE:     MIT (https://spdx.org/licenses/MIT)
4  * PURPOSE:     Test for floating point conversion
5  * COPYRIGHT:   Copyright 2024 Timo Kreuzer <timo.kreuzer@reactos.org>
6  */
7 
8 #include <apitest.h>
9 
10 /* Note: There are 2 behaviors for float to unsigned integer conversion:
11  * 1. The old behavior, which exists only on x86 and CL versions up to somewhere
12  *    between 19.40.33811 and 19.41.33923:
13  *     - If a negative float is cast to an unsigned integer, the result is ULONG_MAX
14  *       for uint32 and ULLONG_MAX for uint64.
15  *     - If a float is cast to an unsigned long and the value is larger than ULONG_MAX,
16  *       the result is ULONG_MAX.
17  * 2. The new behavior (all x64 and x86 versions after the ones mentioned above):
18  *     - If a negative float is cast to an unsigned integer, the result is the same as
19          first casting to the signed type, then casting to the unsigned type.
20  *     - If a float is cast to an unsigned integer and the value is too large for the type,
21  *       the result is 0 for uint32 and 0x8000000000000000 for uint64.
22  * In the old version, the float to unsigned conversion was inlined and used _ftoul2 and
23  * a comparison against 0x43e0000000000000 (9223372036854775808.0) to check for overflow.
24  * In the new version, the float to unsigned conversion is done by a call to _ftoul2_legacy,
25  * which checks for overflow by comparing against 0x5f800000 (18446744073709551616.0) and
26  * then forwards the call to either _ftol2 or _ftoul2.
27  */
28 
29 #if defined(_M_IX86) && defined(__VS_PROJECT__) && (_MSC_FULL_VER < 194133923)
30 #define OLD_BEHAVIOR
31 #endif
32 
33 #ifdef OLD_BEHAVIOR
34 #define ULONG_OVERFLOW ULONG_MAX
35 #define ULONGLONG_OVERFLOW ULLONG_MAX
36 #else
37 #define ULONG_OVERFLOW 0ul
38 #define ULONGLONG_OVERFLOW 0x8000000000000000ull
39 #endif
40 
41 #ifdef __GNUC__
42 #define todo_gcc todo_ros
43 #else
44 #define todo_gcc
45 #endif
46 
47 __declspec(noinline)
48 long cast_float_to_long(float f)
49 {
50     return (long)f;
51 }
52 
53 __declspec(noinline)
54 unsigned long cast_float_to_ulong(float f)
55 {
56     return (unsigned long)f;
57 }
58 
59 __declspec(noinline)
60 long cast_double_to_long(double d)
61 {
62     return (long)d;
63 }
64 
65 __declspec(noinline)
66 unsigned long cast_double_to_ulong(double d)
67 {
68     return (unsigned long)d;
69 }
70 
71 __declspec(noinline)
72 long long cast_float_to_longlong(float f)
73 {
74     return (long long)f;
75 }
76 
77 __declspec(noinline)
78 unsigned long long cast_float_to_ulonglong(float f)
79 {
80     return (unsigned long long)f;
81 }
82 
83 __declspec(noinline)
84 long long cast_double_to_longlong(double d)
85 {
86     return (long long)d;
87 }
88 
89 __declspec(noinline)
90 unsigned long long cast_double_to_ulonglong(double d)
91 {
92     return (unsigned long long)d;
93 }
94 
95 void Test_float(void)
96 {
97     // float to long cast
98     ok_eq_long(cast_float_to_long(0.0f), 0l);
99     ok_eq_long(cast_float_to_long(1.0f), 1l);
100     ok_eq_long(cast_float_to_long(-1.0f), -1l);
101     ok_eq_long(cast_float_to_long(0.5f), 0l);
102     ok_eq_long(cast_float_to_long(-0.5f), 0l);
103     ok_eq_long(cast_float_to_long(0.999999f), 0l);
104     ok_eq_long(cast_float_to_long(-0.999999f), 0l);
105     ok_eq_long(cast_float_to_long(2147483500.0f), 2147483520l);
106     ok_eq_long(cast_float_to_long(2147483583.999f), 2147483520l);
107     ok_eq_long(cast_float_to_long(-2147483583.999f), -2147483520l);
108     ok_eq_long(cast_float_to_long(2147483584.0f), LONG_MIN); // -2147483648
109     ok_eq_long(cast_float_to_long(2147483648.0f), LONG_MIN); // -2147483648
110     ok_eq_long(cast_float_to_long(-2147483648.0f), LONG_MIN); // -2147483648
111     ok_eq_long(cast_float_to_long(10000000000.0f), LONG_MIN);
112     ok_eq_long(cast_float_to_long(-10000000000.0f), LONG_MIN);
113 
114     // float to unsigned long cast (positive values)
115     ok_eq_ulong(cast_float_to_ulong(0.0f), 0ul);
116     ok_eq_ulong(cast_float_to_ulong(1.0f), 1ul);
117     ok_eq_ulong(cast_float_to_ulong(0.5f), 0ul);
118     ok_eq_ulong(cast_float_to_ulong(0.999999f), 0ul);
119     ok_eq_ulong(cast_float_to_ulong(2147483648.0f), 2147483648ul); // 0x80000000
120     ok_eq_ulong(cast_float_to_ulong(4294967150.0f), 4294967040ul); // 0xFFFFFF00
121     ok_eq_ulong(cast_float_to_ulong(4294967294.0f), ULONG_OVERFLOW);
122 
123     // float to unsigned long cast (negative values)
124     ok_eq_ulong(cast_float_to_ulong(-0.0f), 0ul);
125     ok_eq_ulong(cast_float_to_ulong(-0.5f), 0ul);
126     ok_eq_ulong(cast_float_to_ulong(-1.0f), ULONG_MAX);
127 #ifdef OLD_BEHAVIOR
128     ok_eq_ulong(cast_float_to_ulong(-10.0f), ULONG_MAX);
129     ok_eq_ulong(cast_float_to_ulong(-1147483648.0f), ULONG_MAX);
130     ok_eq_ulong(cast_float_to_ulong(-2147483648.0f), ULONG_MAX);
131 #else
132     ok_eq_ulong(cast_float_to_ulong(-10.0f), (unsigned long)-10);
133     ok_eq_ulong(cast_float_to_ulong(-1147483648.0f), (unsigned long)-1147483648ll);
134     ok_eq_ulong(cast_float_to_ulong(-2147483648.0f), (unsigned long)-2147483648ll);
135 #endif
136 
137     // float to long long cast
138     ok_eq_longlong(cast_float_to_longlong(0.0f), 0ll);
139     ok_eq_longlong(cast_float_to_longlong(1.0f), 1ll);
140     ok_eq_longlong(cast_float_to_longlong(-1.0f), -1ll);
141     ok_eq_longlong(cast_float_to_longlong(0.5f), 0ll);
142     ok_eq_longlong(cast_float_to_longlong(-0.5f), 0ll);
143     ok_eq_longlong(cast_float_to_longlong(0.999999f), 0ll);
144     ok_eq_longlong(cast_float_to_longlong(-0.999999f), 0ll);
145     ok_eq_longlong(cast_float_to_longlong(9223371761976868863.9999f), 9223371487098961920ll);
146     ok_eq_longlong(cast_float_to_longlong(9223371761976868864.0f), LLONG_MIN);
147     ok_eq_longlong(cast_float_to_longlong(-9223371761976868863.9999f), -9223371487098961920ll);
148     ok_eq_longlong(cast_float_to_longlong(-9223371761976868864.0f), LLONG_MIN);
149     ok_eq_longlong(cast_float_to_longlong(100000000000000000000.0f), LLONG_MIN);
150     ok_eq_longlong(cast_float_to_longlong(-100000000000000000000.0f), LLONG_MIN);
151 
152     // float to unsigned long long cast (positive values)
153     ok_eq_ulonglong(cast_float_to_ulonglong(0.0f), 0ull);
154     ok_eq_ulonglong(cast_float_to_ulonglong(1.0f), 1ull);
155     ok_eq_ulonglong(cast_float_to_ulonglong(0.5f), 0ull);
156     ok_eq_ulonglong(cast_float_to_ulonglong(0.999999f), 0ull);
157     ok_eq_ulonglong(cast_float_to_ulonglong(9223371487098961920.0f), 9223371487098961920ull); // 0x7FFFFF8000000000
158     ok_eq_ulonglong(cast_float_to_ulonglong(9223372036854775808.0f), 9223372036854775808ull); // 0x8000000000000000
159     ok_eq_ulonglong(cast_float_to_ulonglong(18446743523953737727.9f), 18446742974197923840ull); // 0xFFFFFF0000000000
160     todo_gcc ok_eq_ulonglong(cast_float_to_ulonglong(18446743523953737728.0f), ULONGLONG_OVERFLOW); // 0x8000000000000000 / 0xFFFFFFFFFFFFFFFF
161     todo_gcc ok_eq_ulonglong(cast_float_to_ulonglong(20000000000000000000.0f), ULONGLONG_OVERFLOW); // 0x8000000000000000 / 0xFFFFFFFFFFFFFFFF
162 
163     // float to unsigned long long cast (negative values)
164     ok_eq_ulonglong(cast_float_to_ulonglong(-0.0f), 0ull);
165     ok_eq_ulonglong(cast_float_to_ulonglong(-0.5f), 0ull);
166     ok_eq_ulonglong(cast_float_to_ulonglong(-1.0f), 18446744073709551615ull);
167 #ifdef OLD_BEHAVIOR
168     ok_eq_ulonglong(cast_float_to_ulonglong(-10.0f), ULLONG_MAX);
169     ok_eq_ulonglong(cast_float_to_ulonglong(-1147483648.0f), ULLONG_MAX);
170     ok_eq_ulonglong(cast_float_to_ulonglong(-2147483648.0f), ULLONG_MAX);
171     ok_eq_ulonglong(cast_float_to_ulonglong(-9223371761976868863.9f), ULLONG_MAX);
172     ok_eq_ulonglong(cast_float_to_ulonglong(-9223371761976868864.0f), ULLONG_MAX);
173     ok_eq_ulonglong(cast_float_to_ulonglong(-9223372036854775808.0f), ULLONG_MAX);
174 #else
175     ok_eq_ulonglong(cast_float_to_ulonglong(-10.0f), (unsigned long long)-10);
176     ok_eq_ulonglong(cast_float_to_ulonglong(-1147483648.0f), (unsigned long long)-1147483648ll);
177     ok_eq_ulonglong(cast_float_to_ulonglong(-2147483648.0f), (unsigned long long)-2147483648ll);
178     ok_eq_ulonglong(cast_float_to_ulonglong(-9223371761976868863.9f), (unsigned long long)-9223371487098961920);
179     ok_eq_ulonglong(cast_float_to_ulonglong(-9223371761976868864.0f), (unsigned long long)(-9223372036854775807ll - 1)); // 0x8000000000000000 / ULONGLONG_OVERFLOW
180     ok_eq_ulonglong(cast_float_to_ulonglong(-9223372036854775808.0f), (unsigned long long)(-9223372036854775807ll - 1)); // 0x8000000000000000 / ULONGLONG_OVERFLOW
181 #endif
182     ok_eq_ulonglong(cast_float_to_ulonglong(-100000000000000000000.0f), ULONGLONG_OVERFLOW);
183 }
184 
185 void Test_double(void)
186 {
187     // double to long cast
188     ok_eq_long(cast_double_to_long(0.0), 0l);
189     ok_eq_long(cast_double_to_long(1.0), 1l);
190     ok_eq_long(cast_double_to_long(-1.0), -1l);
191     ok_eq_long(cast_double_to_long(0.5), 0l);
192     ok_eq_long(cast_double_to_long(-0.5), 0l);
193     ok_eq_long(cast_double_to_long(0.999999999), 0l);
194     ok_eq_long(cast_double_to_long(-0.999999999), 0l);
195     ok_eq_long(cast_double_to_long(2147483647.99999), 2147483647l);
196     ok_eq_long(cast_double_to_long(-2147483647.99999), -2147483647l);
197     ok_eq_long(cast_double_to_long(2147483648.0), LONG_MIN); // -2147483648
198     ok_eq_long(cast_double_to_long(-2147483648.0), LONG_MIN); // -2147483648
199     ok_eq_long(cast_double_to_long(10000000000.0), LONG_MIN);
200     ok_eq_long(cast_double_to_long(-10000000000.0), LONG_MIN);
201 
202     // double to unsigned long cast (positive values)
203     ok_eq_ulong(cast_double_to_ulong(0.0), 0ul);
204     ok_eq_ulong(cast_double_to_ulong(1.0), 1ul);
205     ok_eq_ulong(cast_double_to_ulong(0.5), 0ul);
206     ok_eq_ulong(cast_double_to_ulong(0.999999999), 0ul);
207     ok_eq_ulong(cast_double_to_ulong(2147483648.0), 2147483648ul); // 0x80000000
208     ok_eq_ulong(cast_double_to_ulong(4294967295.0), 4294967295ul); // 0xFFFFFFFF
209     ok_eq_ulong(cast_double_to_ulong(4294967296.0), ULONG_OVERFLOW);
210 
211     // double to unsigned long cast (negative values)
212     ok_eq_ulong(cast_double_to_ulong(-0.0), 0ul);
213     ok_eq_ulong(cast_double_to_ulong(-0.5), 0ul);
214     ok_eq_ulong(cast_double_to_ulong(-1.0), ULONG_MAX);
215 #ifdef OLD_BEHAVIOR
216     ok_eq_ulong(cast_double_to_ulong(-10.0), ULONG_MAX);
217     ok_eq_ulong(cast_double_to_ulong(-1147483648.0), ULONG_MAX);
218     ok_eq_ulong(cast_double_to_ulong(-2147483648.0), ULONG_MAX);
219 #else
220     ok_eq_ulong(cast_double_to_ulong(-10.0), (unsigned long)-10);
221     ok_eq_ulong(cast_double_to_ulong(-1147483648.0), (unsigned long)-1147483648ll);
222     ok_eq_ulong(cast_double_to_ulong(-2147483648.0), (unsigned long)-2147483648ll);
223 #endif
224 
225     // double to long long cast
226     ok_eq_longlong(cast_double_to_longlong(0.0), 0ll);
227     ok_eq_longlong(cast_double_to_longlong(1.0), 1ll);
228     ok_eq_longlong(cast_double_to_longlong(-1.0), -1ll);
229     ok_eq_longlong(cast_double_to_longlong(0.5), 0ll);
230     ok_eq_longlong(cast_double_to_longlong(-0.5), 0ll);
231     ok_eq_longlong(cast_double_to_longlong(0.999999), 0ll);
232     ok_eq_longlong(cast_double_to_longlong(-0.999999), 0ll);
233     ok_eq_longlong(cast_double_to_longlong(9223372036854775295.9), 9223372036854774784ll);
234     ok_eq_longlong(cast_double_to_longlong(9223372036854775296.0), LLONG_MIN);
235     ok_eq_longlong(cast_double_to_longlong(-9223372036854775295.9), -9223372036854774784ll);
236     ok_eq_longlong(cast_double_to_longlong(-9223372036854775296.0), LLONG_MIN);
237     ok_eq_longlong(cast_double_to_longlong(100000000000000000000.0), LLONG_MIN);
238     ok_eq_longlong(cast_double_to_longlong(-100000000000000000000.0), LLONG_MIN);
239 
240     // double to unsigned long long cast (positive values)
241     ok_eq_ulonglong(cast_double_to_ulonglong(0.0), 0ull);
242     ok_eq_ulonglong(cast_double_to_ulonglong(1.0), 1ull);
243     ok_eq_ulonglong(cast_double_to_ulonglong(0.5), 0ull);
244     ok_eq_ulonglong(cast_double_to_ulonglong(0.999999), 0ull);
245     ok_eq_ulonglong(cast_double_to_ulonglong(9223372036854774784.0), 9223372036854774784ull); // 0x7FFFFFFFFFFFFC00
246     ok_eq_ulonglong(cast_double_to_ulonglong(9223372036854775808.0), 9223372036854775808ull); // 0x8000000000000000
247     ok_eq_ulonglong(cast_double_to_ulonglong(18446744073709550591.9), 18446744073709549568ull); // 0xFFFFFFFFFFFFF800
248     todo_gcc ok_eq_ulonglong(cast_double_to_ulonglong(18446744073709550592.0), ULONGLONG_OVERFLOW); // 0x8000000000000000 / 0xFFFFFFFFFFFFFFFF
249     todo_gcc ok_eq_ulonglong(cast_double_to_ulonglong(18446744073709551616.0), ULONGLONG_OVERFLOW); // 0x8000000000000000 / 0xFFFFFFFFFFFFFFFF
250     todo_gcc ok_eq_ulonglong(cast_double_to_ulonglong(20000000000000000000.0), ULONGLONG_OVERFLOW); // 0x8000000000000000 / 0xFFFFFFFFFFFFFFFF
251 
252     // float to unsigned long long cast (negative values)
253     ok_eq_ulonglong(cast_double_to_ulonglong(-0.0), 0ull);
254     ok_eq_ulonglong(cast_double_to_ulonglong(-0.5), 0ull);
255     ok_eq_ulonglong(cast_double_to_ulonglong(-1.0), 18446744073709551615ull);
256 #ifdef OLD_BEHAVIOR
257     ok_eq_ulonglong(cast_double_to_ulonglong(-10.0), ULLONG_MAX);
258     ok_eq_ulonglong(cast_double_to_ulonglong(-1147483648.0), ULLONG_MAX);
259     ok_eq_ulonglong(cast_double_to_ulonglong(-2147483648.0), ULLONG_MAX);
260     ok_eq_ulonglong(cast_double_to_ulonglong(-9223371761976868863.9), ULLONG_MAX);
261     ok_eq_ulonglong(cast_double_to_ulonglong(-9223371761976868864.0), ULLONG_MAX);
262     ok_eq_ulonglong(cast_double_to_ulonglong(-9223372036854775808.0), ULLONG_MAX);
263 #else
264     ok_eq_ulonglong(cast_double_to_ulonglong(-10.0), (unsigned long long)-10);
265     ok_eq_ulonglong(cast_double_to_ulonglong(-1147483648.0), (unsigned long long)-1147483648ll);
266     ok_eq_ulonglong(cast_double_to_ulonglong(-2147483648.0), (unsigned long long)-2147483648ll);
267     ok_eq_ulonglong(cast_double_to_ulonglong(-9223372036854775000.0), (unsigned long long)-9223372036854774784ll);
268     ok_eq_ulonglong(cast_double_to_ulonglong(-9223372036854775808.0), (unsigned long long)(-9223372036854775807ll - 1));
269 #endif
270     ok_eq_ulonglong(cast_double_to_ulonglong(-100000000000000000000.0), ULONGLONG_OVERFLOW);
271 }
272 
273 START_TEST(floatconv)
274 {
275     Test_float();
276     Test_double();
277 }
278