Jump to content

Detecting Infinity


Mario Marengo

Recommended Posts

Hi all,

I have a situation where certain shading operations (in VEX) can result in the dreaded floating-point infinity ('inf'), and I need to catch it so I can replace it with a large (but non-inf) value. Specifically, the operation a / b can overflow because a can sometimes get quite large (though never 'inf'), and b may at the same time become quite small (though always >0). So even though the two operands are valid, the result overflows and produces an 'inf' value.

Sadly, there's no isinf() function in VEX so I need some algorithm to deal with it (and I'd rather not go the DSO route just for this little utility). Right now I'm working around it with something very ugly/hacky:

float out = b<(a*epsilon) ? large_value : a/b;

This seems to work for the specific context I need to solve right now (with epsilon=1e-4 and largeval=1e4), but it seems terribly fragile and I have close to zero confidence in it as a generic approach.

Any ideas on how this kind of thing should be handled in a robust way?

TIA.

Link to comment
Share on other sites

Hi all,

I have a situation where certain shading operations (in VEX) can result in the dreaded floating-point infinity ('inf'), and I need to catch it so I can replace it with a large (but non-inf) value. Specifically, the operation a / b can overflow because a can sometimes get quite large (though never 'inf'), and b may at the same time become quite small (though always >0). So even though the two operands are valid, the result overflows and produces an 'inf' value.

Sadly, there's no isinf() function in VEX so I need some algorithm to deal with it (and I'd rather not go the DSO route just for this little utility). Right now I'm working around it with something very ugly/hacky:

float out = b<(a*epsilon) ? large_value : a/b;

This seems to work for the specific context I need to solve right now (with epsilon=1e-4 and largeval=1e4), but it seems terribly fragile and I have close to zero confidence in it as a generic approach.

Any ideas on how this kind of thing should be handled in a robust way?

TIA.

I don't think that SESI will add the feature to VEX, but if you use the HDK, you can write a VEX op which uses SYSalmostEqual() (SYS/SYS_Math.h).

The comment in the header refers you to http://www.cygnus-software.com/papers/comp...aringfloats.htm

Link to comment
Share on other sites

I don't think that SESI will add the feature to VEX, but if you use the HDK, you can write a VEX op which uses SYSalmostEqual() (SYS/SYS_Math.h).

The comment in the header refers you to http://www.cygnus-software.com/papers/comp...aringfloats.htm

Thanks crunch!

I hadn't come across this unsigned-int test method for floats. Very cool.

He claims that in his fp-as-unsigned-int representation, FP_MAX and 'inf' are adjacent, so I'm guessing you're suggesting something like this?

//.. pseudo vex op...
float isinf(float val, int ulps=50) {
   return SYSalmostEqual( val, numeric_limits&lt;float&gt;::max(), ulps);
}

... here I'm assuming VEX_TYPE_FLOAT is the same as the compiler's float. And I guess the SYS_math default distance of 50 floating point numbers is probably reasonable for this distance from 'inf' test...

I was hoping to stay away from a DSO, but I think you're right, this is probably the only way to do it properly.

Cheers!

Link to comment
Share on other sites

I was hoping to stay away from a DSO, but I think you're right, this is probably the only way to do it properly.

This doesn't count as doing it properly, but:

x == x * 1e-45 + 1

is true when x == inf or -inf and false otherwise

Link to comment
Share on other sites

For some reason I thought that you could do something like:

if( (testme -testme) != 0)
{
  printf("variable 'testme' is an inf.");
}

or

if( testme != testme )
{
  printf("variable 'testme' is an inf.");
}

..that is, if the optimizer doesn't catch it?

Or am I thinking of NaNs?

Link to comment
Share on other sites

This doesn't count as doing it properly, but:

x == x * 1e-45 + 1

is true when x == inf or -inf and false otherwise

Interesting. I'll give it a try.

One question: the magic 45 exponent?

Dawson gives the Inf bit pattern (for 32 bit floats) as 0x7F800000. I'm curious if your expression ends up being close to

(x & 0x7FFFFFFF) == 0x7F800000.

Thanks stabby.

@Jason: Thanks for the suggestion. Yeah, I'm pretty sure that one's for NaN's -- NaN's aren't equal (or comparable) to anything, including themselves.

P.S: The dso version is working fine -- I just wrote Dawson's Inf test on it's own since I don't need a full equality comparison. Here's the inf test from crunch's reference (code by Bruce Dawson)

inline bool IsInfinite(float A)
{
	const kInfAsInt = 0x7F800000;

	// An infinity has an exponent of 255 (shift left 23 positions) and
	// a zero mantissa. There are two infinities - positive and negative.
	if ((*(int*)&amp;A &amp; 0x7FFFFFFF) == kInfAsInt)
		return true;
	return false;
}

That's obviously for 32 bit floats, but easily extended to 64.

Link to comment
Share on other sites

Woa! Wait a second!

There's more going on here than I thought...

The dso inf test was catching all infinities just fine over in C++, but now that I try it out in a shader, I'm seeing inf values (as reported by mplay) all over the place... and the test value is just 1e5 (!) -- waaaay below floating point infinity.

surface testInf ( float val = 1e5) {
   Cf = val;
}

But then I noticed that I was rendering with the default 16-bit float quantization. So I switched to full 32-bit quantization and... no more inf's. At 32-bit quantization, inf's (peppered with a few NaN's) start appearing at 1e39...

So inf seems to be a moving target in the shading context, as it seems to depend on quantization... though I'm surprised at the 16-bit float behaviour. I would have expected anything above the maximum-representable fpreal16 (but below fpreal32's infinity) to just show up as fpreal16's maximum value (not inf) -- that goes for both sides of the sign. This is the case for the 8-bit quantization, where +inf's just show up as 255, as I'd expect...

Crap. This is going to be more complicated than I thought :(

Link to comment
Share on other sites

Woa! Wait a second!

...

So inf seems to be a moving target in the shading context, as it seems to depend on quantization... though I'm surprised at the 16-bit float behaviour. I would have expected anything above the maximum-representable fpreal16 (but below fpreal32's infinity) to just show up as fpreal16's maximum value (not inf) -- that goes for both sides of the sign. This is the case for the 8-bit quantization, where +inf's just show up as 255, as I'd expect...

Crap. This is going to be more complicated than I thought :(

But, that's all just dependent on the quantization.

I don't know exactly why you're doing the infinity testing, but I don't think you should be doing it based on quantization. If you're doing it in a shader, if there's a reflection of the surface, you aren't going to get the right levels (since you'd have a fraction of infinity). Basically, you'd be clamping infinities before quantization.

But again, I'm not exactly sure why you're doing this.

Link to comment
Share on other sites

But, that's all just dependent on the quantization.

I don't know exactly why you're doing the infinity testing, but I don't think you should be doing it based on quantization. If you're doing it in a shader, if there's a reflection of the surface, you aren't going to get the right levels (since you'd have a fraction of infinity). Basically, you'd be clamping infinities before quantization.

But again, I'm not exactly sure why you're doing this.

Yes, I agree that I shouldn't be clamping according to quantization. It's more a case of me finding out (after the fact) that the speckles I was seeing in a float16 render, and which showed up as inf values in mplay (leading me down the testing for inf road), don't show up in a float32 render... so I'm starting to rethink the whole issue.

The original reason for testing for infinity:

1. A mult-lobe Cook-Torrance-like BRDF, in a physically correct formulation, generates large reflectance values for small (but >0) roughness values, at certain angles.

2. The results I was getting from my shader agreed with those of the paper I'm trying to implement, except for a few random super-bright speckles which mplay reported as having a value of 'inf'. (this is what tripped me)

3. I made sure that there were no illegal operations anywhere (like div-by-zero, bad params to sqrt(), exp(), inverse trig functions, etc), and finally tracked it down to the BRDF (and associated PDF) simply generating very large values in some cases.

Now I'm thinking that actual overflows were probably happening very rarely (if ever), and I was just lead astray by the inf reported by mplay (which, as you say, is dependent on quantization and doesn't necessarily reflect an internal overflow in the shader... except when rendering float32 I guess).

So... I was just about to try clamping the BRDF's output to some maximum value. That may be all I need to do in the end...

Link to comment
Share on other sites

Yes, I agree that I shouldn't be clamping according to quantization. It's more a case of me finding out (after the fact) that the speckles I was seeing in a float16 render, and which showed up as inf values in mplay (leading me down the testing for inf road), don't show up in a float32 render... so I'm starting to rethink the whole issue.

The original reason for testing for infinity:

1. A mult-lobe Cook-Torrance-like BRDF, in a physically correct formulation, generates large reflectance values for small (but >0) roughness values, at certain angles.

2. The results I was getting from my shader agreed with those of the paper I'm trying to implement, except for a few random super-bright speckles which mplay reported as having a value of 'inf'. (this is what tripped me)

3. I made sure that there were no illegal operations anywhere (like div-by-zero, bad params to sqrt(), exp(), inverse trig functions, etc), and finally tracked it down to the BRDF (and associated PDF) simply generating very large values in some cases.

Now I'm thinking that actual overflows were probably happening very rarely (if ever), and I was just lead astray by the inf reported by mplay (which, as you say, is dependent on quantization and doesn't necessarily reflect an internal overflow in the shader... except when rendering float32 I guess).

So... I was just about to try clamping the BRDF's output to some maximum value. That may be all I need to do in the end...

You might want to look at the vm_colorlimit parameter.

http://odforce.net/wiki/index.php/IFD_Version_9

When performing shading, mantra places no limits on values which may be returned. However, when performing Physically Based Rendering, it's possible to get very large values for some rays. These extrema will show cause color spikes which cannot be smoothed out without sending huge numbers of rays. The color limit is used to clamp the value of the Cf variable to avoid these spikes.
Link to comment
Share on other sites

You might want to look at the vm_colorlimit parameter.

http://odforce.net/wiki/index.php/IFD_Version_9

Yup. That's useful -- forgot about that one.

Well, the infinity thing turned out to be a bit of a red herring -- not completely though, I did manage to generate a few real overflows which I can now handle. But I ended up removing almost all the artifacts by carefully clamping the more numerically unstable parts of the algorithm... trying not to dim the overall output in the process... whew!... man, physical correctness sucks :P

Thanks so much for all the great help crunch, stabby, and Jason!

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...