Computing big numbers in shaders of WebGL/OpenGL

Started by
6 comments, last by alvaro 10 years, 4 months ago

Hello.

I'm in need of computing floats larger than 32-bit in my vertex-shaders.


float phi = index* inc;                     // big number, "5476389.695241543"-biggish
float cosPhi = cos(phi);                

phi is unnecessary big, contains a lot of trigonometric loops and the important decimal precision are removed as it's a 32-bit float. Is there a way to remove all the unnecessary 2*PI loops on phi before it get saved to the float?

Advertisement
So, mod then? I haven't worked with shaders, but I believe you're looking for:

float phi = mod( index * inc, 2*pi );
float cosPhi = cos(phi);
How about this?
float index_mod_2pi = mod(index, 2*pi);
float inc_mod_2pi = mod(inc, 2*pi); // EDIT: Fixed typo (thanks Vilem Otte)
float cosPhi = cos(index_mod_2pi * inc_mod_2pi);
Or you may have to use `fmod' instead (I think GLSL has `mod' and HLSL has `fmod', but I am not sure, and either one would work in this case).

Alvaro's solution is good, as it most likely will result in better-precise result (and I'd say enough precise one). I just confirm that yes, GLSL has mod and GLSL fmod. Note that you have a bug in the code you wrote, the second line should actually have 'inc' as 1st argument into mod on 2nd line.

Now to the thing I wanted to write ... in case you would need even more precision, you *can* use double-precision floating point numbers on gpu, in OpenGL it's through extension GL_ARB_gpu_shader_fp64, in HLSL I'm sure that in D3D 11.1 (I also think that 11.0 supports them but I'm not sure -> if someone checking this works with D3D, please confirm), it is possible to use them.

In OpenGL you can use glUniform*d, glUniform*dv, glUniformMatrix*dv and also when GL_EXT_direct_state_access is supported, then glProgramUniform*d, glProgramUniform*dv, glProgramUniformMatrix*dv. Your variables are then declared as double/dvec/dmat in shader.

My current blog on programming, linux and stuff - http://gameprogrammerdiary.blogspot.com

Thanks for replies guys, appreciate it. But...

How about this?


float index_mod_2pi = mod(index, 2*pi);
float inc_mod_2pi = mod(inc, 2*pi); // EDIT: Fixed typo (thanks Vilem Otte)
float cosPhi = cos(index_mod_2pi * inc_mod_2pi);
Or you may have to use `fmod' instead (I think GLSL has `mod' and HLSL has `fmod', but I am not sure, and either one would work in this case).

(A * B) mod C != (A mod C) * (B mod C)....it is = ( (A mod C) * (B mod C) ) mod C

But how is this compiled when you multiply two floats like this and it exceeds 32-bit in the multiplication, can they go over that precision until you save it in a float? I still have precision errors

Thanks for replies guys, appreciate it. But...

How about this?

float index_mod_2pi = mod(index, 2*pi);
float inc_mod_2pi = mod(inc, 2*pi); // EDIT: Fixed typo (thanks Vilem Otte)
float cosPhi = cos(index_mod_2pi * inc_mod_2pi);
Or you may have to use `fmod' instead (I think GLSL has `mod' and HLSL has `fmod', but I am not sure, and either one would work in this case).

(A * B) mod C != (A mod C) * (B mod C)....it is = ( (A mod C) * (B mod C) ) mod C


The last "mod C" is provided by the periodicity of the cosine function, so you don't really need it.

But how is this compiled when you multiply two floats like this and it exceeds 32-bit in the multiplication, can they go over that precision until you save it in a float? I still have precision errors

What do you mean by "it exceeds 32-bit in the multiplication"? What bits are you talking about? Either you don't understand what floats are, or I don't understand what you are saying.
The last "mod C" is provided by the periodicity of the cosine function, so you don't really need it.

Ah cool.

What do you mean by "it exceeds 32-bit in the multiplication"? What bits are you talking about? Either you don't understand what floats are, or I don't understand what you are saying.

Yea I worded it a bit badly. But what I meant was when we do this:


float cosPhi = cos(index_mod_2pi * inc_mod_2pi);

the inner multiplication:


index_mod_2pi * inc_mod_2pi

doesn't this get saved to a 32-bit memory address stored temporarily until we do cos(this_stored_address) hence losing precision, or am I completely wrong? Because I seem to get much higher precision if I calculate index_mod_2pi * inc_mod_2pi on the CPU with 64-bit addresses and then send it in as uniform or attribute.

Your terminology is imprecise. 32-bit addresses or 64-bit addresses have nothing to do with this discussion. There is a 32-bit floating-point type (a.k.a. "single-precision", or "float") and a 64-bit floating-point type (a.k.a. "double precision", or "double"). The original question used single-precision numbers, and although it is possible to use double-precision numbers in shaders in some circumstances, not every card supports it, and the ones that do have many fewer cores capable of double-precision computation. So we are basically constrained to single-precision computations.

[EDIT: To learn about floating-point types, Google for "IEEE 754 introduction" or "what every computer scientist should know about floating-point arithmetic".]

Within single-precision computations, I think what I posted is about the best you can do.

This topic is closed to new replies.

Advertisement