1 // BEGIN_COPYRIGHT -*- glean -*-
3 // Copyright (C) 1999 Allen Akin All Rights Reserved.
5 // Permission is hereby granted, free of charge, to any person
6 // obtaining a copy of this software and associated documentation
7 // files (the "Software"), to deal in the Software without
8 // restriction, including without limitation the rights to use,
9 // copy, modify, merge, publish, distribute, sublicense, and/or
10 // sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following
14 // The above copyright notice and this permission notice shall be
15 // included in all copies or substantial portions of the
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
19 // KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
20 // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
21 // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ALLEN AKIN BE
22 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
23 // AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
24 // OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 // DEALINGS IN THE SOFTWARE.
29 // Authors: Brian Paul, Keith Whitwell
31 #include "tfpexceptions.h"
35 #define INCLUDE_FPU_CONTROL 0
36 #if INCLUDE_FPU_CONTROL
37 #include <fpu_control.h>
44 // This might be useful at some point
46 FPExceptionsTest::enableExceptions(bool enable
)
48 #if INCLUDE_FPU_CONTROL
49 const fpu_control_t bits
=
57 /* generate FP exceptions */
76 // XXX any endian issues with this???
77 // Works on x86 / little endian
90 make_float(float *dest
, unsigned sign
, unsigned exponent
, unsigned mantissa
)
92 union fi
*destfi
= (union fi
*) dest
;
93 destfi
->bits
.sign
= sign
;
94 destfi
->bits
.exponent
= exponent
;
95 destfi
->bits
.mantissa
= mantissa
;
99 make_denorm_float(float *dest
, int sign
, int mantissa
)
101 make_float(dest
, sign
, 0, mantissa
);
105 make_pos_inf_float(float *dest
)
107 make_float(dest
, 0, 255, 0); // or HUGE_VALF?
111 make_neg_inf_float(float *dest
)
113 make_float(dest
, 1, 255, 0); // or -HUGE_VALF?
117 make_signaling_nan_float(float *dest
)
119 make_float(dest
, 0, 255, 1);
123 make_quiet_nan_float(float *dest
)
125 make_float(dest
, 0, 255, 1 << 22);
129 make_denorm_double(double * /*dest*/, int /*sign*/, int /*mantissa*/)
135 make_pos_inf_double(double *dest
)
141 make_neg_inf_double(double *dest
)
147 make_signaling_nan_double(double * /*dest*/)
153 make_quiet_nan_double(double * /*dest*/)
163 int iexp
, imnt
, isgn
;
166 printf("float %f (%e)\n\tuint 0x%x\n\tsign %d exponent %d mantissa 0x%x\n",
167 fi
.f
, fi
.f
, fi
.ui
, fi
.bits
.sign
, fi
.bits
.exponent
, fi
.bits
.mantissa
);
169 switch (fi
.bits
.exponent
) {
171 if (fi
.bits
.mantissa
== 0)
172 printf("\t%szero\n", fi
.bits
.sign
? "-" : "+");
174 printf("\tdenormalized float\n");
176 iexp
= -126 - 23; /* -149 */
177 imnt
= (int)fi
.bits
.mantissa
;
178 isgn
= fi
.bits
.sign
? -1 : 1;
179 fi2
.f
= isgn
* imnt
* ldexp(1.0, iexp
);
181 printf("\trecombining: %d * 0x%x * 2.0^%d == %f (%e)\n",
182 isgn
, imnt
, iexp
, fi2
.f
, fi2
.f
);
183 printf("\trecombined: sign %d exponent %d mantissa 0x%x\n",
184 fi2
.bits
.sign
, fi2
.bits
.exponent
, fi2
.bits
.mantissa
);
189 if (fi
.bits
.mantissa
& (1<<22))
190 printf("\tQNaN (Quiet NaN/indeterminate value)\n");
191 else if (fi
.bits
.mantissa
)
192 printf("\tSNaN (Signalling NaN/invalid value)\n");
194 printf("\t%sinf\n", fi
.bits
.sign
? "-" : "+");
198 iexp
= fi
.bits
.exponent
- (127 + 23);
199 imnt
= (1<<23) + (int)fi
.bits
.mantissa
;
200 isgn
= fi
.bits
.sign
? -1 : 1;
201 fi2
.f
= isgn
* imnt
* ldexp(1.0, iexp
);
203 printf("\trecombining: %d * 0x%x * 2.0^%d == %f\n",
204 isgn
, imnt
, iexp
, fi2
.f
);
206 printf("\trecombined: sign %d exponent %d mantissa 0x%x\n",
207 fi2
.bits
.sign
, fi2
.bits
.exponent
, fi2
.bits
.mantissa
);
211 /* Let's look and see what would happen if we interpret all these
212 * cases as normal floats:
214 iexp
= fi
.bits
.exponent
- (127 + 23);
215 imnt
= (1<<23) + (int)fi
.bits
.mantissa
;
216 isgn
= fi
.bits
.sign
? -1 : 1;
217 fi2
.f
= isgn
* imnt
* ldexp(1.0, iexp
);
219 printf("\tvalue if treated as normalized: %f (%e)\n",
224 /* Examine some interesting floats
232 for (i
= -3; i
< 10; i
++) {
234 print_float(ldexp(1.0, i
));
237 for (f
= -4 ; f
< 4; f
+= 1)
240 for (f
= -.01 ; f
< .01; f
+= .002)
249 /* Explicitly make a denormal - I've no idea how to create these
250 * with regular calculations:
252 make_float(&f
, 0, 0, 0x1000);
255 /* It seems you can just specify them!
260 /* A little, non-denormalized float
262 make_float(&f
, 0, 1, 0x1);
265 /* A negative little, non-denormalized float
267 make_float(&f
, 1, 1, 0x1);
272 make_float(&f
, 0, 254, ~0);
275 make_float(&f
, 1, 254, ~0);
278 /* Littlest and biggest denormals:
280 make_float(&f
, 0, 0, 1);
282 make_float(&f
, 0, 0, ~0);
286 make_float(&f
, 1, 0, 1);
288 make_float(&f
, 1, 0, ~0);
296 FPExceptionsTest::testVertices(Mode m
)
300 for (int i
= 0; i
< 3; i
++) {
307 // set problematic values
310 make_pos_inf_float(&v
[1][0]);
311 make_neg_inf_float(&v
[2][1]);
314 make_signaling_nan_float(&v
[1][0]);
315 make_quiet_nan_float(&v
[2][1]);
323 make_denorm_float(&v
[0][0], 0, 1);
324 make_denorm_float(&v
[1][1], 1, 1);
339 glColor4fv(v
[0]); glVertex2f(-1, -1);
340 glColor4fv(v
[1]); glVertex2f( 1, -1);
341 glColor4fv(v
[2]); glVertex2f( 0, 1);
345 glEnable(GL_LIGHTING
);
347 glNormal3fv(v
[0]); glVertex2f(-1, -1);
348 glNormal3fv(v
[1]); glVertex2f( 1, -1);
349 glNormal3fv(v
[2]); glVertex2f( 0, 1);
351 glDisable(GL_LIGHTING
);
354 glEnable(GL_TEXTURE_2D
);
356 glTexCoord4fv(v
[0]); glVertex2f(-1, -1);
357 glTexCoord4fv(v
[1]); glVertex2f( 1, -1);
358 glTexCoord4fv(v
[2]); glVertex2f( 0, 1);
360 glDisable(GL_TEXTURE_2D
);
367 FPExceptionsTest::testTransformation(Mode m
)
372 for (int i
= 0; i
< 15; i
++)
374 mat
[0] = mat
[5] = mat
[10] = mat
[15] = 1.0;
376 // set problematic values
379 make_pos_inf_float(&mat
[0]); // X scale
380 make_neg_inf_float(&mat
[13]); // Y translate
383 make_signaling_nan_float(&mat
[0]); // X scale
384 make_quiet_nan_float(&mat
[13]); // Y translate
388 mat
[0] = mat
[5] = mat
[10] = mat
[15] = 0.0;
391 make_denorm_float(&mat
[0], 0, 1);
392 make_denorm_float(&mat
[13], 1, 1);
398 glMatrixMode(GL_MODELVIEW
);
416 FPExceptionsTest::testClipping(Mode m
)
420 // start w/ nice values
421 plane
[0] = plane
[1] = plane
[2] = plane
[3] = 0.0;
423 // set problematic values
426 make_pos_inf_double(&plane
[0]);
427 make_neg_inf_double(&plane
[3]);
430 make_signaling_nan_double(&plane
[0]);
431 make_quiet_nan_double(&plane
[3]);
437 make_denorm_double(&plane
[0], 0, 1);
438 make_denorm_double(&plane
[3], 1, 1);
448 glClipPlane(GL_CLIP_PLANE0
, plane
);
449 glEnable(GL_CLIP_PLANE0
);
458 glDisable(GL_CLIP_PLANE0
);
464 // pass large doubles to OpenGL and see what happens when converted to float.
466 FPExceptionsTest::testOverflow(void)
469 for (int i
= 0; i
< 3; i
++) {
481 for (int i
= 0; i
< 15; i
++)
483 mat
[0] = mat
[5] = mat
[10] = mat
[15] = 1.0e100
;
485 glMatrixMode(GL_MODELVIEW
);
503 FPExceptionsTest::setup(void)
505 // Simple texture map
506 static const GLfloat texImage
[2][2][3] = {
507 { {1, 1, 1}, {0, 0, 0} },
508 { {0, 0, 0}, {1, 1, 1} }
511 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGB
, 2, 2, 0,
512 GL_RGB
, GL_FLOAT
, texImage
);
513 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
514 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
519 glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER
, GL_TRUE
);
524 FPExceptionsTest::reportPassFail(MultiTestResult
&r
,
525 bool pass
, const char *msg
) const
528 if (env
->options
.verbosity
)
529 env
->log
<< name
<< " PASS: " << msg
<< " test\n";
533 if (env
->options
.verbosity
)
534 env
->log
<< name
<< " FAILURE: " << msg
<< " test\n";
540 FPExceptionsTest::runOne(MultiTestResult
&r
, Window
&w
)
546 p
= testVertices(MODE_INFINITY
);
547 reportPassFail(r
, p
, "Infinite value vertex");
549 p
= testVertices(MODE_NAN
);
550 reportPassFail(r
, p
, "NaN value vertex");
552 p
= testVertices(MODE_DIVZERO
);
553 reportPassFail(r
, p
, "Divide by zero vertex");
555 p
= testVertices(MODE_DENORM
);
556 reportPassFail(r
, p
, "Denorm vertex");
559 p
= testTransformation(MODE_INFINITY
);
560 reportPassFail(r
, p
, "Infinite matrix transform");
562 p
= testTransformation(MODE_NAN
);
563 reportPassFail(r
, p
, "NaN matrix transform");
565 p
= testTransformation(MODE_DIVZERO
);
566 reportPassFail(r
, p
, "Zero matrix transform");
568 p
= testTransformation(MODE_DENORM
);
569 reportPassFail(r
, p
, "Denorm matrix transform");
572 p
= testClipping(MODE_INFINITY
);
573 reportPassFail(r
, p
, "Infinite clip plane");
575 p
= testClipping(MODE_NAN
);
576 reportPassFail(r
, p
, "NaN clip plane");
578 p
= testClipping(MODE_DIVZERO
);
579 reportPassFail(r
, p
, "Zero clip plane");
581 p
= testClipping(MODE_DENORM
);
582 reportPassFail(r
, p
, "Denorm clip plane");
584 p
= testClipping(MODE_OVERFLOW
);
585 reportPassFail(r
, p
, "Overflow clip plane");
589 reportPassFail(r
, p
, "Overflow");
591 r
.pass
= (r
.numFailed
== 0);
595 // The test object itself:
596 FPExceptionsTest
FPExceptionsTest("fpexceptions", // test name
597 "window, rgb", // surface/pixel format
598 "", // no extensions required
599 "Test for floating point exceptions caused by +/-infinity, Nan, divide by zero, etc in a number of circumstances.\n");