[Random] uniform_real, min/max at end of value range
Hi there,
from what I can see, uniform_real(min,max) essentially does this:
fp_type result = uniform_fp_between_0_and_1 * (max - min) + min; // (1)
(1) will fail e.g. if the distance between min and max is >
std::numeric_limits
AMDG Ruediger Berlich wrote:
from what I can see, uniform_real(min,max) essentially does this:
fp_type result = uniform_fp_between_0_and_1 * (max - min) + min; // (1)
(1) will fail e.g. if the distance between min and max is > std::numeric_limits
::max() While writing unit tests for my code I have come across exactly this situation. My application would not suffer much from reducing the allowed value range for my variables to < +/-0.5*std::numeric_limits
::max() . However I feel that this would be a bit artificial. Hence I was wondering whether code like the following made more sense (although it is certainly not as efficient):
/**********************************************************/
fp_type uniform_real(const fp_type& min, const fp_type& max) { // Check that min and max have appropriate values assert(min<=max);
if(min >= fp_type(0.) || max <= fp_type(0.)) { // (max-min) is valid return uniform_01() * (max - min) + min; } else { // Some values will fail (max-min) // We know: min<0., max>0. assert(min<0); assert(max>0);
// Calculate a random number in the range [0,1[ fp_type fraction = uniform_01();
// Calculate the fraction of the distance of min from 0. volatile fp_type minFraction = -fraction*min; // Calculate the fraction of the distance of max from 0. volatile fp_type maxFraction = fraction*max; //
// The start of the scale volatile fp_type result = min + minFraction;
// Add maxFraction to the result. result += maxFraction;
return result; } }
I'll have to think about whether this is can cause any odd artifacts, but at a first glance it looks good.
/**********************************************************/
Essentially, in cases where (max-min) could be larger than the allowed value range, the distance to 0 of each max and min is individually multiplied with a fp random number in the range [0,1[ . The resulting values are then added seperately to min. There should be no situation where an attempt is made to create a value larger than the allowed value range ... unless the compiler optimizes this to "result=min+minFraction+maxFraction;" or worse. volatile might or might not prevent this, I am not sure (seems to work with g++ 4.4.3, though).
Actually min+minFraction+maxFraction is parsed as (min+minFraction)+maxFraction, which is okay. What we need to do is make sure that the compiler doesn't play fast and loose with associativity and rewrite this as min+(minFraction+maxFraction). With gcc you can guarantee this with -fno-unsafe-math-optimizations -fno-finite-math-only, which are the default. MSVC has an option for this. I don't think that volatile is likely to help much. I know that MSVC ignores volatile for local variables that don't escape from the function.
What is your view on this ? Are there different/better approaches in Boost that I could use ?
uniform_real should definitely be made bullet-proof if at all possible. In Christ, Steven Watanabe
participants (2)
-
Ruediger Berlich
-
Steven Watanabe