summaryrefslogtreecommitdiff
path: root/tests/glean/tfpexceptions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/glean/tfpexceptions.cpp')
-rw-r--r--tests/glean/tfpexceptions.cpp602
1 files changed, 602 insertions, 0 deletions
diff --git a/tests/glean/tfpexceptions.cpp b/tests/glean/tfpexceptions.cpp
new file mode 100644
index 000000000..7a93eda1e
--- /dev/null
+++ b/tests/glean/tfpexceptions.cpp
@@ -0,0 +1,602 @@
+// BEGIN_COPYRIGHT -*- glean -*-
+//
+// Copyright (C) 1999 Allen Akin All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the
+// Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+// KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ALLEN AKIN BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+// OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+// END_COPYRIGHT
+
+// Authors: Brian Paul, Keith Whitwell
+
+#include "tfpexceptions.h"
+#include <cassert>
+#include <cmath>
+
+#define INCLUDE_FPU_CONTROL 0
+#if INCLUDE_FPU_CONTROL
+#include <fpu_control.h>
+#endif
+
+
+namespace GLEAN {
+
+
+// This might be useful at some point
+void
+FPExceptionsTest::enableExceptions(bool enable)
+{
+#if INCLUDE_FPU_CONTROL
+ const fpu_control_t bits =
+ _FPU_MASK_IM |
+ _FPU_MASK_DM |
+ _FPU_MASK_ZM |
+ _FPU_MASK_OM |
+ _FPU_MASK_UM;
+
+ if (enable) {
+ /* generate FP exceptions */
+ fpu_control_t mask;
+ _FPU_GETCW(mask);
+ mask &= ~bits;
+ _FPU_SETCW(mask);
+ }
+ else {
+ fpu_control_t mask;
+ _FPU_GETCW(mask);
+ mask |= bits;
+ _FPU_SETCW(mask);
+ }
+#else
+ (void) enable;
+#endif
+}
+
+
+
+// XXX any endian issues with this???
+// Works on x86 / little endian
+union fi {
+ float f;
+ struct {
+ unsigned mantissa:23;
+ unsigned exponent:8;
+ unsigned sign:1;
+ } bits;
+ unsigned ui;
+};
+
+
+static void
+make_float(float *dest, unsigned sign, unsigned exponent, unsigned mantissa)
+{
+ union fi *destfi = (union fi *) dest;
+ destfi->bits.sign = sign;
+ destfi->bits.exponent = exponent;
+ destfi->bits.mantissa = mantissa;
+}
+
+static void
+make_denorm_float(float *dest, int sign, int mantissa)
+{
+ make_float(dest, sign, 0, mantissa);
+}
+
+static void
+make_pos_inf_float(float *dest)
+{
+ make_float(dest, 0, 255, 0); // or HUGE_VALF?
+}
+
+static void
+make_neg_inf_float(float *dest)
+{
+ make_float(dest, 1, 255, 0); // or -HUGE_VALF?
+}
+
+static void
+make_signaling_nan_float(float *dest)
+{
+ make_float(dest, 0, 255, 1);
+}
+
+static void
+make_quiet_nan_float(float *dest)
+{
+ make_float(dest, 0, 255, 1 << 22);
+}
+
+static void
+make_denorm_double(double * /*dest*/, int /*sign*/, int /*mantissa*/)
+{
+ // XXX to do
+}
+
+static void
+make_pos_inf_double(double *dest)
+{
+ *dest = HUGE_VAL;
+}
+
+static void
+make_neg_inf_double(double *dest)
+{
+ *dest = -HUGE_VAL;
+}
+
+static void
+make_signaling_nan_double(double * /*dest*/)
+{
+ // XXX to do
+}
+
+static void
+make_quiet_nan_double(double * /*dest*/)
+{
+ // XXX to do
+}
+
+
+static void
+print_float(float f)
+{
+ union fi fi, fi2;
+ int iexp, imnt, isgn;
+
+ fi.f = f;
+ printf("float %f (%e)\n\tuint 0x%x\n\tsign %d exponent %d mantissa 0x%x\n",
+ fi.f, fi.f, fi.ui, fi.bits.sign, fi.bits.exponent, fi.bits.mantissa);
+
+ switch (fi.bits.exponent) {
+ case 0:
+ if (fi.bits.mantissa == 0)
+ printf("\t%szero\n", fi.bits.sign ? "-" : "+");
+ else {
+ printf("\tdenormalized float\n");
+
+ iexp = -126 - 23; /* -149 */
+ imnt = (int)fi.bits.mantissa;
+ isgn = fi.bits.sign ? -1 : 1;
+ fi2.f = isgn * imnt * ldexp(1.0, iexp);
+
+ printf("\trecombining: %d * 0x%x * 2.0^%d == %f (%e)\n",
+ isgn, imnt, iexp, fi2.f, fi2.f);
+ printf("\trecombined: sign %d exponent %d mantissa 0x%x\n",
+ fi2.bits.sign, fi2.bits.exponent, fi2.bits.mantissa);
+ }
+ break;
+
+ case 255:
+ if (fi.bits.mantissa & (1<<22))
+ printf("\tQNaN (Quiet NaN/indeterminate value)\n");
+ else if (fi.bits.mantissa)
+ printf("\tSNaN (Signalling NaN/invalid value)\n");
+ else
+ printf("\t%sinf\n", fi.bits.sign ? "-" : "+");
+ break;
+
+ default:
+ iexp = fi.bits.exponent - (127 + 23);
+ imnt = (1<<23) + (int)fi.bits.mantissa;
+ isgn = fi.bits.sign ? -1 : 1;
+ fi2.f = isgn * imnt * ldexp(1.0, iexp);
+
+ printf("\trecombining: %d * 0x%x * 2.0^%d == %f\n",
+ isgn, imnt, iexp, fi2.f);
+
+ printf("\trecombined: sign %d exponent %d mantissa 0x%x\n",
+ fi2.bits.sign, fi2.bits.exponent, fi2.bits.mantissa);
+ break;
+ }
+
+ /* Let's look and see what would happen if we interpret all these
+ * cases as normal floats:
+ */
+ iexp = fi.bits.exponent - (127 + 23);
+ imnt = (1<<23) + (int)fi.bits.mantissa;
+ isgn = fi.bits.sign ? -1 : 1;
+ fi2.f = isgn * imnt * ldexp(1.0, iexp);
+
+ printf("\tvalue if treated as normalized: %f (%e)\n",
+ fi2.f, fi2.f);
+}
+
+
+/* Examine some interesting floats
+ */
+#if 0
+int main()
+{
+ float f;
+ int i;
+
+ for (i = -3; i < 10; i++) {
+ printf("%d:\n ", i);
+ print_float(ldexp(1.0, i));
+ }
+
+ for (f = -4 ; f < 4; f += 1)
+ print_float(f);
+
+ for (f = -.01 ; f < .01; f += .002)
+ print_float(f);
+
+ f = 1.0/0;
+ print_float(f);
+
+ f += 1.0;
+ print_float(f);
+
+ /* Explicitly make a denormal - I've no idea how to create these
+ * with regular calculations:
+ */
+ make_float(&f, 0, 0, 0x1000);
+ print_float(f);
+
+ /* It seems you can just specify them!
+ */
+ f = 5.739719e-42;
+ print_float(f);
+
+ /* A little, non-denormalized float
+ */
+ make_float(&f, 0, 1, 0x1);
+ print_float(f);
+
+ /* A negative little, non-denormalized float
+ */
+ make_float(&f, 1, 1, 0x1);
+ print_float(f);
+
+ /* A big float
+ */
+ make_float(&f, 0, 254, ~0);
+ print_float(f);
+
+ make_float(&f, 1, 254, ~0);
+ print_float(f);
+
+ /* Littlest and biggest denormals:
+ */
+ make_float(&f, 0, 0, 1);
+ print_float(f);
+ make_float(&f, 0, 0, ~0);
+ print_float(f);
+
+
+ make_float(&f, 1, 0, 1);
+ print_float(f);
+ make_float(&f, 1, 0, ~0);
+ print_float(f);
+
+}
+#endif
+
+
+bool
+FPExceptionsTest::testVertices(Mode m)
+{
+ float v[3][4];
+ // nice coords
+ for (int i = 0; i < 3; i++) {
+ v[i][0] = 0.0;
+ v[i][1] = 0.0;
+ v[i][2] = 0.0;
+ v[i][3] = 1.0;
+ }
+
+ // set problematic values
+ switch (m) {
+ case MODE_INFINITY:
+ make_pos_inf_float(&v[1][0]);
+ make_neg_inf_float(&v[2][1]);
+ break;
+ case MODE_NAN:
+ make_signaling_nan_float(&v[1][0]);
+ make_quiet_nan_float(&v[2][1]);
+ break;
+ case MODE_DIVZERO:
+ v[0][3] = 0.0;
+ v[1][3] = 0.0;
+ v[2][3] = 0.0;
+ break;
+ case MODE_DENORM:
+ make_denorm_float(&v[0][0], 0, 1);
+ make_denorm_float(&v[1][1], 1, 1);
+ break;
+ default:
+ ; // nothing
+ }
+
+ // vertex positions
+ glBegin(GL_POLYGON);
+ glVertex4fv(v[0]);
+ glVertex4fv(v[1]);
+ glVertex4fv(v[2]);
+ glEnd();
+
+ // colors
+ glBegin(GL_POLYGON);
+ glColor4fv(v[0]); glVertex2f(-1, -1);
+ glColor4fv(v[1]); glVertex2f( 1, -1);
+ glColor4fv(v[2]); glVertex2f( 0, 1);
+ glEnd();
+
+ // normals
+ glEnable(GL_LIGHTING);
+ glBegin(GL_POLYGON);
+ glNormal3fv(v[0]); glVertex2f(-1, -1);
+ glNormal3fv(v[1]); glVertex2f( 1, -1);
+ glNormal3fv(v[2]); glVertex2f( 0, 1);
+ glEnd();
+ glDisable(GL_LIGHTING);
+
+ // texcoords
+ glEnable(GL_TEXTURE_2D);
+ glBegin(GL_POLYGON);
+ glTexCoord4fv(v[0]); glVertex2f(-1, -1);
+ glTexCoord4fv(v[1]); glVertex2f( 1, -1);
+ glTexCoord4fv(v[2]); glVertex2f( 0, 1);
+ glEnd();
+ glDisable(GL_TEXTURE_2D);
+
+ return true;
+}
+
+
+bool
+FPExceptionsTest::testTransformation(Mode m)
+{
+ float mat[16];
+
+ // identity
+ for (int i = 0; i < 15; i++)
+ mat[i] = 0.0;
+ mat[0] = mat[5] = mat[10] = mat[15] = 1.0;
+
+ // set problematic values
+ switch (m) {
+ case MODE_INFINITY:
+ make_pos_inf_float(&mat[0]); // X scale
+ make_neg_inf_float(&mat[13]); // Y translate
+ break;
+ case MODE_NAN:
+ make_signaling_nan_float(&mat[0]); // X scale
+ make_quiet_nan_float(&mat[13]); // Y translate
+ break;
+ case MODE_DIVZERO:
+ // all zero matrix
+ mat[0] = mat[5] = mat[10] = mat[15] = 0.0;
+ break;
+ case MODE_DENORM:
+ make_denorm_float(&mat[0], 0, 1);
+ make_denorm_float(&mat[13], 1, 1);
+ break;
+ default:
+ ; // nothing
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadMatrixf(mat);
+
+ // vertex positions
+ glBegin(GL_POLYGON);
+ glVertex2f(-1, -1);
+ glVertex2f( 1, -1);
+ glVertex2f( 0, 1);
+ glEnd();
+
+ glPopMatrix();
+
+ return true;
+}
+
+
+bool
+FPExceptionsTest::testClipping(Mode m)
+{
+ double plane[4];
+
+ // start w/ nice values
+ plane[0] = plane[1] = plane[2] = plane[3] = 0.0;
+
+ // set problematic values
+ switch (m) {
+ case MODE_INFINITY:
+ make_pos_inf_double(&plane[0]);
+ make_neg_inf_double(&plane[3]);
+ break;
+ case MODE_NAN:
+ make_signaling_nan_double(&plane[0]);
+ make_quiet_nan_double(&plane[3]);
+ break;
+ case MODE_DIVZERO:
+ // nothing
+ break;
+ case MODE_DENORM:
+ make_denorm_double(&plane[0], 0, 1);
+ make_denorm_double(&plane[3], 1, 1);
+ break;
+ case MODE_OVERFLOW:
+ plane[0] = 1.0e300;
+ plane[3] = 1.0e-300;
+ break;
+ default:
+ ; // nothing
+ }
+
+ glClipPlane(GL_CLIP_PLANE0, plane);
+ glEnable(GL_CLIP_PLANE0);
+
+ // vertex positions
+ glBegin(GL_POLYGON);
+ glVertex2f(-1, -1);
+ glVertex2f( 1, -1);
+ glVertex2f( 0, 1);
+ glEnd();
+
+ glDisable(GL_CLIP_PLANE0);
+
+ return true;
+}
+
+
+// pass large doubles to OpenGL and see what happens when converted to float.
+bool
+FPExceptionsTest::testOverflow(void)
+{
+ GLdouble v[3][4];
+ for (int i = 0; i < 3; i++) {
+ v[i][0] = 0.0;
+ v[i][1] = 0.0;
+ v[i][2] = 0.0;
+ v[i][3] = 1.0;
+ }
+ v[0][0] = 1.0e300;
+ v[0][1] = -1.0e300;
+ v[1][0] = 1.0e-300;
+ v[1][1] = 1.0e-300;
+
+ GLdouble mat[16];
+ for (int i = 0; i < 15; i++)
+ mat[i] = 0.0;
+ mat[0] = mat[5] = mat[10] = mat[15] = 1.0e500;
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadMatrixd(mat);
+
+ glBegin(GL_POLYGON);
+ glVertex4dv(v[0]);
+ glVertex4dv(v[1]);
+ glVertex4dv(v[2]);
+ glEnd();
+
+ glPopMatrix();
+
+ return true;
+}
+
+
+
+void
+FPExceptionsTest::setup(void)
+{
+ // Simple texture map
+ static const GLfloat texImage[2][2][3] = {
+ { {1, 1, 1}, {0, 0, 0} },
+ { {0, 0, 0}, {1, 1, 1} }
+ };
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0,
+ GL_RGB, GL_FLOAT, texImage);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ // simple lighting
+ glEnable(GL_LIGHT0);
+ glEnable(GL_LIGHT1);
+ glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
+}
+
+
+void
+FPExceptionsTest::reportPassFail(MultiTestResult &r,
+ bool pass, const char *msg) const
+{
+ if (pass) {
+ if (env->options.verbosity)
+ env->log << name << " PASS: " << msg << " test\n";
+ r.numPassed++;
+ }
+ else {
+ if (env->options.verbosity)
+ env->log << name << " FAILURE: " << msg << " test\n";
+ r.numFailed++;
+ }
+}
+
+void
+FPExceptionsTest::runOne(MultiTestResult &r, Window &w)
+{
+ bool p;
+
+ (void) w;
+
+ p = testVertices(MODE_INFINITY);
+ reportPassFail(r, p, "Infinite value vertex");
+
+ p = testVertices(MODE_NAN);
+ reportPassFail(r, p, "NaN value vertex");
+
+ p = testVertices(MODE_DIVZERO);
+ reportPassFail(r, p, "Divide by zero vertex");
+
+ p = testVertices(MODE_DENORM);
+ reportPassFail(r, p, "Denorm vertex");
+
+
+ p = testTransformation(MODE_INFINITY);
+ reportPassFail(r, p, "Infinite matrix transform");
+
+ p = testTransformation(MODE_NAN);
+ reportPassFail(r, p, "NaN matrix transform");
+
+ p = testTransformation(MODE_DIVZERO);
+ reportPassFail(r, p, "Zero matrix transform");
+
+ p = testTransformation(MODE_DENORM);
+ reportPassFail(r, p, "Denorm matrix transform");
+
+
+ p = testClipping(MODE_INFINITY);
+ reportPassFail(r, p, "Infinite clip plane");
+
+ p = testClipping(MODE_NAN);
+ reportPassFail(r, p, "NaN clip plane");
+
+ p = testClipping(MODE_DIVZERO);
+ reportPassFail(r, p, "Zero clip plane");
+
+ p = testClipping(MODE_DENORM);
+ reportPassFail(r, p, "Denorm clip plane");
+
+ p = testClipping(MODE_OVERFLOW);
+ reportPassFail(r, p, "Overflow clip plane");
+
+
+ p = testOverflow();
+ reportPassFail(r, p, "Overflow");
+
+ r.pass = (r.numFailed == 0);
+}
+
+
+// The test object itself:
+FPExceptionsTest FPExceptionsTest("fpexceptions", // test name
+ "window, rgb", // surface/pixel format
+ "", // no extensions required
+ "Test for floating point exceptions caused by +/-infinity, Nan, divide by zero, etc in a number of circumstances.\n");
+
+
+} // namespace GLEAN