1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.renderscript.cts;
18 
19 import android.renderscript.cts.Target;
20 import android.renderscript.cts.Float16Utils;
21 import android.renderscript.RSRuntimeException;
22 import android.util.Log;
23 
24 public class FloatyUnitTest extends RSBaseCompute {
25     static float subnormalFloat = 10000 * Float.MIN_VALUE;
26     static float normalFloat1 = 1.7833920e+16f;
27     static float normalFloat2 = -1.9905756e-16f;
28 
29     static double subnormalDouble = 10000 * Double.MIN_VALUE;
30     static double normalDouble = 1.7833920e+16;
31 
32     static double normalHalf = 1985; // 2048 - 63.  Magic number chosen for use in testHalf128Ulp
33     static double negativeNormalHalf = -237;
34 
35     // Some double values that are precisely representable in half-precision.
36     static double[] preciseFloat16Values = {Double.NaN,
37                                             Double.POSITIVE_INFINITY,
38                                             Double.NEGATIVE_INFINITY,
39                                             0., 1, 2, 74.0625, 3000, 65504,
40                                             Float16Utils.MIN_VALUE,
41                                             Float16Utils.MIN_VALUE * 100,
42                                             -0., -1, -2, -74.0625, -3000, -65504,
43                                             -Float16Utils.MIN_VALUE,
44                                             -Float16Utils.MIN_VALUE * 100,
45                                            };
46 
47     // Fail if Floaty f with an extra error allowance of 'extraAllowedError' doesn't accept 'value'
shouldAccept(Target.Floaty f, double value, double extraAllowedError)48     private void shouldAccept(Target.Floaty f, double value, double extraAllowedError) {
49         if (!f.couldBe(value, extraAllowedError)) {
50             StringBuilder message = new StringBuilder();
51             message.append("Floaty: ");
52             appendVariableToMessage(message, f);
53             message.append("\n");
54             if (extraAllowedError > 0.) {
55                 message.append("extraAllowedError: ");
56                 appendVariableToMessage(message, extraAllowedError);
57                 message.append("\n");
58             }
59             message.append("Value: ");
60             appendVariableToMessage(message, (float) value);
61             message.append("\n");
62             assertTrue("Floaty incorrectly doesn't accept value:\n" + message.toString(), false);
63         }
64     }
65 
66     // Fail if Floaty f doesn't accept value
shouldAccept(Target.Floaty f, double value)67     private void shouldAccept(Target.Floaty f, double value) {
68         shouldAccept(f, value, 0.);
69     }
70 
71     // Fail if Floaty f with an extra error allowance of 'extraAllowedError' accepts 'value'
shouldNotAccept(Target.Floaty f, double value, double extraAllowedError)72     private void shouldNotAccept(Target.Floaty f, double value, double extraAllowedError) {
73         if (f.couldBe(value)) {
74             StringBuilder message = new StringBuilder();
75             message.append("Floaty: ");
76             appendVariableToMessage(message, f);
77             message.append("\n");
78             if (extraAllowedError > 0.) {
79                 message.append("extraAllowedError: ");
80                 appendVariableToMessage(message, extraAllowedError);
81                 message.append("\n");
82             }
83             message.append("Value: ");
84             appendVariableToMessage(message, (float) value);
85             message.append("\n");
86             assertTrue("Floaty incorrectly accepts value:\n" + message.toString(), false);
87         }
88     }
89 
90     // Fail if Floaty f accepts value
shouldNotAccept(Target.Floaty f, double value)91     private void shouldNotAccept(Target.Floaty f, double value) {
92         shouldNotAccept(f, value, 0.);
93     }
94 
95     // Test Target that accepts precise 1ulp error for floating values.
testFloat1Ulp()96     public void testFloat1Ulp() {
97         Target t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.FLOAT, false);
98         t.setPrecision(1, 1);
99 
100         Target.Floaty subnormalFloaty = t.new32(subnormalFloat);
101         Target.Floaty normalFloaty = t.new32(normalFloat1);
102 
103         // for subnormal
104         shouldAccept(subnormalFloaty, (double) subnormalFloat);
105         shouldAccept(subnormalFloaty, (double) subnormalFloat + Math.ulp(subnormalFloat));
106         shouldAccept(subnormalFloaty, (double) subnormalFloat - Math.ulp(subnormalFloat));
107         shouldNotAccept(subnormalFloaty, (double) subnormalFloat + 2 * Math.ulp(subnormalFloat));
108         shouldNotAccept(subnormalFloaty, (double) subnormalFloat - 2 * Math.ulp(subnormalFloat));
109         shouldNotAccept(subnormalFloaty, (double) normalFloat1);
110 
111         // for normalFloaty
112         shouldAccept(normalFloaty, (double) normalFloat1);
113         shouldAccept(normalFloaty, (double) normalFloat1 + Math.ulp(normalFloat1));
114         shouldAccept(normalFloaty, (double) normalFloat1 - Math.ulp(normalFloat1));
115         shouldNotAccept(normalFloaty, (double) normalFloat1 + 2 * Math.ulp(normalFloat1));
116         shouldNotAccept(normalFloaty, (double) normalFloat1 - 2 * Math.ulp(normalFloat1));
117         shouldNotAccept(normalFloaty, (double) subnormalFloat);
118     }
119 
120     // Test Target that accepts precise 8192ulp error for floating values.
testFloat8192Ulp()121     public void testFloat8192Ulp() {
122         Target t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.FLOAT, false);
123         t.setPrecision(8192, 8192);
124 
125         Target.Floaty subnormalFloaty = t.new32(subnormalFloat);
126         Target.Floaty normalFloaty = t.new32(normalFloat2);
127 
128         // for subnormalFloaty
129         shouldAccept(subnormalFloaty, (double) subnormalFloat);
130         shouldAccept(subnormalFloaty, (double) subnormalFloat + 8192 * Math.ulp(subnormalFloat));
131         shouldAccept(subnormalFloaty, (double) subnormalFloat - 8192 * Math.ulp(subnormalFloat));
132         shouldNotAccept(subnormalFloaty, (double) subnormalFloat + 8193 * Math.ulp(subnormalFloat));
133         shouldNotAccept(subnormalFloaty, (double) subnormalFloat - 8193 * Math.ulp(subnormalFloat));
134         shouldNotAccept(subnormalFloaty, (double) normalFloat1);
135 
136         // for normalFloaty
137         shouldAccept(normalFloaty, (double) normalFloat2);
138         shouldAccept(normalFloaty, (double) normalFloat2 + 8192 * Math.ulp(normalFloat2));
139         shouldAccept(normalFloaty, (double) normalFloat2 - 8192 * Math.ulp(normalFloat2));
140         shouldNotAccept(normalFloaty, (double) normalFloat2 + 8193 * Math.ulp(normalFloat2));
141         shouldNotAccept(normalFloaty, (double) normalFloat2 - 8193 * Math.ulp(normalFloat2));
142         shouldNotAccept(normalFloaty, (double) subnormalFloat);
143     }
144 
145     // Test Target that accepts relaxed 1ulp error for floating values.
testFloat1UlpRelaxed()146     public void testFloat1UlpRelaxed() {
147         Target t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.FLOAT, true);
148         t.setPrecision(1, 1);
149 
150         Target.Floaty subnormalFloaty = t.new32(subnormalFloat);
151 
152         // for subnormal
153         shouldAccept(subnormalFloaty, (double) subnormalFloat);
154         // In relaxed mode, Floaty uses the smallest normal as the ULP if ULP is subnormal.
155         shouldAccept(subnormalFloaty, (double) Float.MIN_NORMAL + Float.MIN_NORMAL);
156         shouldAccept(subnormalFloaty, (double) 0.f - Float.MIN_NORMAL);
157         shouldNotAccept(subnormalFloaty, (double) Float.MIN_NORMAL + 2 * Float.MIN_NORMAL);
158         shouldNotAccept(subnormalFloaty, (double) 0.f - 2 * Float.MIN_NORMAL);
159         shouldNotAccept(subnormalFloaty, (double) normalFloat1);
160     }
161 
162     // Test Target that accepts relaxed 8192ulp error for floating values.
testFloat8192UlpRelaxed()163     public void testFloat8192UlpRelaxed() {
164         Target t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.FLOAT, true);
165         t.setPrecision(8192, 8192);
166 
167         Target.Floaty subnormalFloaty = t.new32(subnormalFloat);
168 
169         // for subnormalFloaty
170         shouldAccept(subnormalFloaty, (double) subnormalFloat);
171         // In relaxed mode, Floaty uses the smallest normal as the ULP if ULP is subnormal.
172         shouldAccept(subnormalFloaty, (double) Float.MIN_NORMAL + 8192 * Float.MIN_NORMAL);
173         shouldAccept(subnormalFloaty, (double) 0.f - 8192 * Float.MIN_NORMAL);
174         shouldNotAccept(subnormalFloaty, (double) Float.MIN_NORMAL + 8193 * Float.MIN_NORMAL);
175         shouldNotAccept(subnormalFloaty, (double) 0.f - 8193 * Float.MIN_NORMAL);
176         shouldNotAccept(subnormalFloaty, (double) normalFloat1);
177     }
178 
179     // Test Target that accepts precise 1ulp error for double values.
testDouble1Ulp()180     public void testDouble1Ulp() {
181         Target t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.DOUBLE, false);
182         t.setPrecision(1, 1);
183 
184         Target.Floaty subnormalFloaty = t.new64(subnormalDouble);
185         Target.Floaty normalFloaty = t.new64(normalDouble);
186 
187         // for subnormal
188         shouldAccept(subnormalFloaty, subnormalDouble);
189         shouldAccept(subnormalFloaty, subnormalDouble + Math.ulp(subnormalDouble));
190         shouldAccept(subnormalFloaty, subnormalDouble - Math.ulp(subnormalDouble));
191         shouldNotAccept(subnormalFloaty, subnormalDouble + 2 * Math.ulp(subnormalDouble));
192         shouldNotAccept(subnormalFloaty, subnormalDouble - 2 * Math.ulp(subnormalDouble));
193         shouldNotAccept(subnormalFloaty, normalDouble);
194 
195         // for normalFloaty
196         shouldAccept(normalFloaty, normalDouble);
197         shouldAccept(normalFloaty, normalDouble + Math.ulp(normalDouble));
198         shouldAccept(normalFloaty, normalDouble - Math.ulp(normalDouble));
199         shouldNotAccept(normalFloaty, normalDouble + 2 * Math.ulp(normalDouble));
200         shouldNotAccept(normalFloaty, normalDouble - 2 * Math.ulp(normalDouble));
201         shouldNotAccept(normalFloaty, subnormalDouble);
202     }
203 
204     // Test Target that accepts precise 8192ulp error for double values.
testDouble8192Ulp()205     public void testDouble8192Ulp() {
206         Target t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.DOUBLE, false);
207         t.setPrecision(8192, 8192);
208 
209         Target.Floaty subnormalFloaty = t.new64(subnormalDouble);
210         Target.Floaty normalFloaty = t.new64(normalDouble);
211 
212         // for subnormal
213         shouldAccept(subnormalFloaty, subnormalDouble);
214         shouldAccept(subnormalFloaty, subnormalDouble + 8192 * Math.ulp(subnormalDouble));
215         shouldAccept(subnormalFloaty, subnormalDouble - 8192 * Math.ulp(subnormalDouble));
216         shouldNotAccept(subnormalFloaty, subnormalDouble + 8193 * Math.ulp(subnormalDouble));
217         shouldNotAccept(subnormalFloaty, subnormalDouble - 8193 * Math.ulp(subnormalDouble));
218         shouldNotAccept(subnormalFloaty, normalDouble);
219 
220         // for normalFloaty
221         shouldAccept(normalFloaty, normalDouble);
222         shouldAccept(normalFloaty, normalDouble + 8192 * Math.ulp(normalDouble));
223         shouldAccept(normalFloaty, normalDouble - 8192 * Math.ulp(normalDouble));
224         shouldNotAccept(normalFloaty, normalDouble + 8193 * Math.ulp(normalDouble));
225         shouldNotAccept(normalFloaty, normalDouble - 8193 * Math.ulp(normalDouble));
226         shouldNotAccept(normalFloaty, subnormalDouble);
227     }
228 
229     // Test Target that accepts precise 1ulp error for half values.
testHalf1Ulp()230     public void testHalf1Ulp() {
231         Target t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.HALF, false);
232         t.setPrecision(1, 1);
233 
234         Target.Floaty normalFloaty = t.newFloaty(normalHalf);
235         shouldAccept(normalFloaty, normalHalf);
236         shouldAccept(normalFloaty, normalHalf + Float16Utils.float16Ulp(normalHalf));
237         shouldAccept(normalFloaty, normalHalf - Float16Utils.float16Ulp(normalHalf));
238         shouldNotAccept(normalFloaty, normalHalf + 2 * Float16Utils.float16Ulp(normalHalf));
239         shouldNotAccept(normalFloaty, normalHalf - 2 * Float16Utils.float16Ulp(normalHalf));
240         shouldNotAccept(normalFloaty, 2500);
241     }
242 
243     // Test Target that accepts precise 128ulp error for half values.
testHalf128Ulp()244     public void testHalf128Ulp() {
245         Target t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.HALF, false);
246         t.setPrecision(128, 128);
247 
248         Target.Floaty normalFloaty = t.newFloaty(normalHalf);
249         shouldAccept(normalFloaty, normalHalf);
250         shouldAccept(normalFloaty, normalHalf + 128 * Float16Utils.float16Ulp(normalHalf));
251         shouldAccept(normalFloaty, normalHalf - 128 * Float16Utils.float16Ulp(normalHalf));
252         shouldNotAccept(normalFloaty, normalHalf - 129 * Float16Utils.float16Ulp(normalHalf));
253 
254         // Since normalHalf + 128ULP has a higher exponent, Floaty accepts an extra ULP to maintin
255         // the invariant that the range is always precisely representable in Float16.
256         shouldAccept(normalFloaty, normalHalf + 129 * Float16Utils.float16Ulp(normalHalf));
257         shouldNotAccept(normalFloaty, normalHalf + 130 * Float16Utils.float16Ulp(normalHalf));
258         shouldNotAccept(normalFloaty, 2500);
259     }
260 
testExtraAllowedError()261     public void testExtraAllowedError() {
262         Target t;
263         double extraError, lb, ub;
264 
265         t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.FLOAT, false);
266         t.setPrecision(4, 4);
267 
268         // Test normal float value with extraAllowedError
269         extraError = 1e-15;
270         Target.Floaty normalFloaty = t.newFloaty(normalFloat2);
271         ub = normalFloat2 + 4 * Math.ulp(normalFloat2) + extraError;
272         lb = normalFloat2 - 4 * Math.ulp(normalFloat2) - extraError;
273         shouldAccept(normalFloaty, ub, extraError);
274         shouldAccept(normalFloaty, lb, extraError);
275         shouldNotAccept(normalFloaty, ub + Math.ulp(ub), extraError);
276         shouldNotAccept(normalFloaty, lb - Math.ulp(lb), extraError);
277 
278         t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.FLOAT, false);
279         t.setPrecision(2, 2);
280         extraError = Float.MIN_VALUE;
281 
282         // Test subnormal float value with extraAllowedError
283         Target.Floaty subnormalFloaty = t.newFloaty(subnormalFloat);
284         ub = subnormalFloat + 2 * Math.ulp(subnormalFloat) + extraError;
285         lb = subnormalFloat - 2 * Math.ulp(subnormalFloat) - extraError;
286         shouldAccept(subnormalFloaty, ub, extraError);
287         shouldAccept(subnormalFloaty, lb, extraError);
288         shouldNotAccept(subnormalFloaty, ub + Math.ulp(ub), extraError);
289         shouldNotAccept(subnormalFloaty, lb - Math.ulp(lb), extraError);
290 
291         t = new Target(Target.FunctionType.NATIVE, Target.ReturnType.HALF, false);
292         t.setPrecision(0, 0);
293 
294         // Test Float16 with extraAllowedError in same order of magnitude
295         extraError = 2.0;
296         Target.Floaty halfFloaty = t.newFloaty(normalHalf);
297         ub = normalHalf + extraError;
298         lb = normalHalf - extraError;
299         shouldAccept(halfFloaty, ub, extraError);
300         shouldAccept(halfFloaty, lb, extraError);
301         shouldNotAccept(halfFloaty, ub + Float16Utils.float16Ulp(ub), extraError);
302         shouldNotAccept(halfFloaty, lb - Float16Utils.float16Ulp(lb), extraError);
303 
304         // Test Float16 with a tiny extraAllowedError
305         extraError = Float16Utils.MIN_NORMAL;
306         ub = normalHalf;
307         lb = normalHalf;
308         shouldAccept(halfFloaty, ub, extraError);
309         shouldAccept(halfFloaty, lb, extraError);
310         shouldNotAccept(halfFloaty, ub + Float16Utils.float16Ulp(ub), extraError);
311         shouldNotAccept(halfFloaty, lb - Float16Utils.float16Ulp(lb), extraError);
312 
313         // Test negative Float16 with extraAllowedError in same order of magnitude
314         extraError = 2.0;
315         Target.Floaty negativeHalfFloaty = t.newFloaty(negativeNormalHalf);
316         ub = negativeNormalHalf + extraError;
317         lb = negativeNormalHalf - extraError;
318         shouldAccept(negativeHalfFloaty, ub, extraError);
319         shouldAccept(negativeHalfFloaty, lb, extraError);
320         shouldNotAccept(negativeHalfFloaty, ub + Float16Utils.float16Ulp(ub), extraError);
321         shouldNotAccept(negativeHalfFloaty, lb - Float16Utils.float16Ulp(lb), extraError);
322 
323         // Test negative Float16 with a tiny extraAllowedError
324         extraError = Float16Utils.MIN_NORMAL;
325         ub = negativeNormalHalf;
326         lb = negativeNormalHalf;
327         shouldAccept(negativeHalfFloaty, ub, extraError);
328         shouldAccept(negativeHalfFloaty, lb, extraError);
329         shouldNotAccept(negativeHalfFloaty, ub + Float16Utils.float16Ulp(ub), extraError);
330         shouldNotAccept(negativeHalfFloaty, lb - Float16Utils.float16Ulp(lb), extraError);
331     }
332 
333     // Test that range of allowed error is trimmed at the zero boundary.  This function tests both
334     // float, double, and half Targets.
testRangeDoesNotAcrossZero()335     public void testRangeDoesNotAcrossZero() {
336         Target t;
337         Target.Floaty floaty;
338 
339         t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.FLOAT, false);
340         t.setPrecision(4, 4);
341 
342         floaty = t.new32(Float.MIN_VALUE);
343         shouldAccept(floaty, (double) Float.MIN_VALUE);
344         shouldAccept(floaty, (double) Float.MIN_VALUE + 4 * Float.MIN_VALUE);
345         shouldAccept(floaty, (double) 0.f);
346         shouldNotAccept(floaty, (double) 0.f - Float.MIN_VALUE);
347 
348         floaty = t.new32(-Float.MIN_VALUE);
349         shouldAccept(floaty, (double) -Float.MIN_VALUE);
350         shouldAccept(floaty, (double) -Float.MIN_VALUE - 4 * Float.MIN_VALUE);
351         shouldAccept(floaty, (double) 0.f);
352         shouldNotAccept(floaty, (double) 0.f + Float.MIN_VALUE);
353 
354         t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.DOUBLE, false);
355         t.setPrecision(4, 4);
356 
357         floaty = t.new64(Double.MIN_VALUE);
358         shouldAccept(floaty, Double.MIN_VALUE);
359         shouldAccept(floaty, Double.MIN_VALUE + 4 * Double.MIN_VALUE);
360         shouldAccept(floaty, 0.);
361         shouldNotAccept(floaty, 0. - Double.MIN_VALUE);
362 
363         floaty = t.new64(-Double.MIN_VALUE);
364         shouldAccept(floaty, -Double.MIN_VALUE);
365         shouldAccept(floaty, -Double.MIN_VALUE - 4 * Double.MIN_VALUE);
366         shouldAccept(floaty, 0.);
367         shouldNotAccept(floaty, 0. + Double.MIN_VALUE);
368 
369         t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.HALF, false);
370         t.setPrecision(4, 4);
371 
372         // Subnormals are not required to be handled for Float16.  Test with MIN_NORMAL instead.
373         floaty = t.newFloaty(Float16Utils.MIN_NORMAL);
374         shouldAccept(floaty, Float16Utils.MIN_NORMAL);
375         shouldAccept(floaty, Float16Utils.MIN_NORMAL+ 4 * Double.MIN_NORMAL);
376         shouldAccept(floaty, 0.);
377         shouldNotAccept(floaty, 0. - Float16Utils.MIN_NORMAL);
378 
379         floaty = t.newFloaty(-Float16Utils.MIN_NORMAL);
380         shouldAccept(floaty, -Float16Utils.MIN_NORMAL);
381         shouldAccept(floaty, -Float16Utils.MIN_NORMAL- 4 * Float16Utils.MIN_NORMAL);
382         shouldAccept(floaty, 0.f);
383         shouldNotAccept(floaty, 0. + Float16Utils.MIN_NORMAL);
384     }
385 
386     // Validate float16Ulp for a double value that is precisely representable in half-precision.
validateFloat16Ulp(double value)387     private void validateFloat16Ulp(double value) {
388         double absoluteValue = Math.abs(value);
389         double ulp = Float16Utils.float16Ulp(value);
390 
391         if (absoluteValue == 0.) {
392             assertEquals(Float16Utils.MIN_VALUE, ulp);
393             return;
394         }
395         if (Double.isNaN(absoluteValue)) {
396             assertTrue("Ulp for NaN is not NaN", Double.isNaN(ulp));
397             return;
398         }
399         if (absoluteValue == Double.POSITIVE_INFINITY ||
400             absoluteValue == Double.NEGATIVE_INFINITY) {
401             assertEquals(Double.POSITIVE_INFINITY, ulp);
402             return;
403         }
404 
405         if (absoluteValue < Math.scalb(1., -24)) {
406             assertTrue("Input double value smaller than MIN_VALUE for Float16", false);
407         }
408 
409         if (absoluteValue < Math.scalb(1., -14)) {
410             // Input is subnormal Float16.  Ulp should be 2^-24
411             assertEquals(Math.scalb(1., -24), ulp);
412             return;
413         }
414 
415         boolean tested = false;
416         int exponent = -13;
417         double limit = Math.scalb(1., exponent);
418         for (; exponent <= 16; exponent ++, limit *= 2) {
419             if (absoluteValue < limit) {
420                 assertEquals(ulp, Math.scalb(1., exponent - 11));
421                 tested = true;
422                 break;
423             }
424         }
425         assertTrue("Ulp not validated.  Absolute value " + Double.toString(absoluteValue), tested);
426     }
427 
428     // Test float16Ulp for all valid inputs (i.e. all Float16 values represented as Double) and test
429     // that assertions fire for Double values that are not representable in Float16.
testFloat16Ulp()430     public void testFloat16Ulp() {
431         // Test float16Ulp for all short values.
432         for (short s = Short.MIN_VALUE; ; s ++) {
433             validateFloat16Ulp(Float16Utils.convertFloat16ToDouble(s));
434             // Detect loop termination here.  Doing so in the for statement creates an infinite
435             // loop.
436             if (s == Short.MAX_VALUE) {
437                 break;
438             }
439         }
440 
441         // Test float16Ulp for some known values
442         for (double value: preciseFloat16Values) {
443             validateFloat16Ulp(value);
444         }
445 
446         // Expect an exception for values not representable in Float16.  The loop below tests this
447         // for all values in this array and their negation.
448         double valuesOutOfFloat16Range[] = {Math.scalb(1., -100),
449                                             Double.valueOf("0x1.8p-24"),
450                                             Double.valueOf("0x1.ffffffffp-20"),
451                                             1024.55, 65520., 66000.
452                                            };
453 
454         for (double value: valuesOutOfFloat16Range) {
455             try {
456                 Float16Utils.float16Ulp(value);
457                 assertTrue("Expected exception not thrown for: " + Double.toString(value), false);
458             } catch (RSRuntimeException e) {
459                 // Ignore the expected exception.
460                 // Log.w("testFloat16Ulp", "Received expected exception: " + e.getMessage());
461             }
462 
463             try {
464                 Float16Utils.float16Ulp(-value);
465                 assertTrue("Expected exception not thrown for: " + Double.toString(value), false);
466             } catch (RSRuntimeException e) {
467                 // Ignore the expected exception.
468                 // Log.w("testFloat16Ulp", "Received expected exception: " + e.getMessage());
469             }
470         }
471     }
472 
validateRoundToFloat16(double argument, double low, double high)473     private void validateRoundToFloat16(double argument, double low, double high) {
474         double[] result = Float16Utils.roundToFloat16(argument);
475         double[] expected = new double[]{low, high};
476 
477         for (int i = 0; i < 2; i ++) {
478             if (result[i] == expected[i])
479                 continue;
480             if (Double.isNaN(result[i]) && Double.isNaN(expected[i]))
481                 continue;
482 
483             StringBuilder message = new StringBuilder();
484             message.append("For input ");
485             appendVariableToMessage(message, argument);
486             message.append("\n");
487             message.append("For field " + Integer.toString(i) + ": Expected output: ");
488             appendVariableToMessage(message, expected[i]);
489             message.append("\n");
490             message.append("Actual output: ");
491             appendVariableToMessage(message, result[i]);
492             message.append("\n");
493             assertTrue("roundToFloat16 error:\n" + message.toString(), false);
494         }
495     }
496 
testRoundToFloat16()497     public void testRoundToFloat16() {
498         // Validate values that are precisely representable in Float16.  The bounds have to be the
499         // same as the input.
500         for (double value: preciseFloat16Values) {
501             validateRoundToFloat16(value, value, value);
502         }
503 
504         // Validate for special cases:
505         //   Double.MIN_VALUE is between Float16 0 and MIN_VALUE
506         //   Double.MAX_VALUE is between Float16 MAX_VALUE and +Infinity
507         //   65519 and 65520 are between Float16 MAX_VALUE and +Infinity (but their exponent is
508         //       within Float16's range).
509         //   3001.5 and 3000.5 are between Float16 3000 and 3002
510         validateRoundToFloat16(Double.MIN_VALUE, 0.0, Float16Utils.MIN_VALUE);
511         validateRoundToFloat16(Double.MAX_VALUE, Double.POSITIVE_INFINITY,
512                                Double.POSITIVE_INFINITY);
513         validateRoundToFloat16(65519., 65504., Double.POSITIVE_INFINITY);
514         validateRoundToFloat16(65520., 65504., Double.POSITIVE_INFINITY);
515         validateRoundToFloat16(3001.5, 3000., 3002.);
516         validateRoundToFloat16(3000.5, 3000., 3002.);
517 
518         validateRoundToFloat16(-Double.MIN_VALUE, -Float16Utils.MIN_VALUE, -0.0);
519         validateRoundToFloat16(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY,
520                                Double.NEGATIVE_INFINITY);
521         validateRoundToFloat16(-65519., Double.NEGATIVE_INFINITY, -65504.);
522         validateRoundToFloat16(-65520., Double.NEGATIVE_INFINITY, -65504.);
523         validateRoundToFloat16(-3001.5, -3002., -3000.);
524         validateRoundToFloat16(-3000.5, -3002., -3000.);
525 
526         // Validate that values precisely between two Float16 values get handled properly.
527         double[] float16Sample = {0., 1., 100.,
528                                   Float16Utils.MIN_VALUE,
529                                   Float16Utils.MIN_VALUE * 100,
530                                   Float16Utils.MIN_NORMAL * 100
531                                  };
532 
533         for (double value: float16Sample) {
534             double ulp = Float16Utils.float16Ulp(value);
535 
536             validateRoundToFloat16(value + 0.5 * ulp, value, value + ulp);
537             validateRoundToFloat16(-value - 0.5 * ulp, -value - ulp, -value);
538         }
539     }
540 
testConvertDoubleToFloat16()541     public void testConvertDoubleToFloat16() {
542         assertEquals(Float16Utils.convertDoubleToFloat16(0.), (short) 0x0);
543         assertEquals(Float16Utils.convertDoubleToFloat16(Float16Utils.MIN_VALUE), (short) 0x0001);
544         assertEquals(Float16Utils.convertDoubleToFloat16(42 * Float16Utils.MIN_VALUE), (short) 0x002a);
545         assertEquals(Float16Utils.convertDoubleToFloat16(Float16Utils.MIN_NORMAL), (short) 0x400);
546         assertEquals(Float16Utils.convertDoubleToFloat16(42.), (short) 0x5140);
547         assertEquals(Float16Utils.convertDoubleToFloat16(1024.), (short) 0x6400);
548         assertEquals(Float16Utils.convertDoubleToFloat16(Double.POSITIVE_INFINITY), (short) 0x7c00);
549 
550         assertEquals(Float16Utils.convertDoubleToFloat16(-0.), (short) 0x8000);
551         assertEquals(Float16Utils.convertDoubleToFloat16(-Float16Utils.MIN_VALUE), (short) 0x8001);
552         assertEquals(Float16Utils.convertDoubleToFloat16(-42 * Float16Utils.MIN_VALUE), (short) 0x802a);
553         assertEquals(Float16Utils.convertDoubleToFloat16(-Float16Utils.MIN_NORMAL), (short) 0x8400);
554         assertEquals(Float16Utils.convertDoubleToFloat16(-42.), (short) 0xd140);
555         assertEquals(Float16Utils.convertDoubleToFloat16(-1024.), (short) 0xe400);
556         assertEquals(Float16Utils.convertDoubleToFloat16(Double.NEGATIVE_INFINITY), (short) 0xfc00);
557 
558         assertEquals(Float16Utils.convertDoubleToFloat16(Double.NaN), (short) 0x7e00);
559     }
560 }
561