Convert float to UInt32 - which expression is more accurate

I have a number float xthat should be in the range <0,1>, but it undergoes several numerical operations - the result may be a little outside of <0,1>.

I need to convert this result to uint yusing the whole range UInt32. Of course, I need to pinch xin the range <0,1> and scale it.

But which order of operations is better?

y = (uint)round(min(max(x, 0.0F), 1.0F) * UInt32.MaxValue)

or

y = (uint)round(min(max(x * UInt32.MaxValue, 0.0F), UInt32.MaxValue)

In other words, is it better to scale first, then pinch OR pinch, and then scale? I am not very deep in IEEE floating point representation, but I believe that there is a difference in the order in which the above expressions are calculated.

+4
source share
4 answers

Since the multiplication to get from [0.0f .. 1.0f] to [0 .. UInt32.MaxValue] itself can be approximative, the order of operations, which obviously has obviously the property that you want, is multiplied, then clamped, then round.

The maximum value for commit is a float immediately below 2 32 that is 4294967040.0f. Although this number is several units lower than UInt32.MaxValue, which allows any larger value to mean conversion overflow by UInt32.

Any of the lines below should work:

y = (uint)round(min(max(x * 4294967040.0F, 0.0F), 4294967040.0F))

UInt32.MaxValue. , (, , 4294967040 , 1.0f, ) 4294967040 1.0f .


[0.0f.. 1.0f] , , , :

y = (uint)round(min(max(x, 0.0F), 1.0F) * 4294967040.0F)

, , UInt32.MaxValue:

if (x <= 0.0f) y = 0
else if (x < 0.5f) y = (uint) round (x * 4294967296.0F)
else if (x >= 1.0f) y = UInt32.MaxValue
else y = UInt32.MaxValue - (uint) round ((1.0f - x) * 4294967296.0F)

, x y, ( 0,5f) UInt32.MaxValue. , , , . , , 0.0f 1.0f, 0.5f, :

if (x < 0.5f)
{
  if (x <= 0.0f) y = ...
  else y = ...
}
else
{
  if (x >= 1.0f) y = ...
  else y = ...
}
+3

:

  • - ( 0.0 → 0 1.0 → 2 ^ 32-1 )
  • , , , .
  • , .

, , , , , , . , uint32, , .

, # OpenCL. OpenCL - , , , (, , , , OpenCL , ), :

convert_uint_sat(x * 0x1.0p32f)

#; #, :

if (x <= 0.0F) y = UInt32.MinValue;
else if (x >= 1.0F) y = UInt32.MaxValue;
else y = (uint)Math.Truncate(x * 4294967296.0F);
+2

, x [0,1], , - UInt32, .. UInt32 . , .. .

.:

var y = (UInt32) (Math.Min(Math.Max(x, 0f), 1f) * UInt32.MaxValue);

, , . , .

0

Single cannot maintain sufficient accuracy to maintain an intermediate result, so you need to scale and then clamp, but you cannot hold UInt32.MaxValue because it cannot be represented as a single. The maximum UInt32 that you can safely secure is 4294967167

from this code here

        Single maxUInt32 = (Single)UInt32.MaxValue;
        Double accurateValue = maxUInt32;
        while (true)
        {
            accurateValue -= 1;
            Single temp = (Single)accurateValue;
            Double temp2 = (Double)temp;
            if (temp2 < (Double)UInt32.MaxValue)
                break;
        }

See this test ...

        Double val1 = UInt32.MaxValue;
        Double val2 = val1 + 1;

        Double valR = val2 / val1;

        Single sValR = (Single)valR;

        //Straight Scale and Cast
        UInt32 NewValue = (UInt32)(sValR * UInt32.MaxValue);
        //Result = 0;

        //Clamp Then Scale Then Cast
        UInt32 NewValue2 = (UInt32)(Math.Min(sValR, 1.0f) * UInt32.MaxValue);
        //Result = 0;

        //Scale Then Clamp Then Cast
        UInt32 NewValue3 = (UInt32)(Math.Min(sValR * UInt32.MaxValue, UInt32.MaxValue));
        //Result = 0;

        //Using Doubles
        //Straight Scale and Cast
        UInt32 NewValue4 = (UInt32)(valR * UInt32.MaxValue);
        //Result = 0;

        //Clamp Then Scale Then Cast
        UInt32 NewValue5 = (UInt32)(Math.Min(valR, 1.0f) * UInt32.MaxValue);
        //Result = 4294967295;

        //Scale Then Clamp Then Cast
        UInt32 NewValue6 = (UInt32)(Math.Min(valR * UInt32.MaxValue, UInt32.MaxValue));
        //Result = 4294967295;

        //Comparing to 4294967167
        //Straight Scale and Cast
        UInt32 NewValue7 = (UInt32)(sValR * UInt32.MaxValue);
        //Result = 0;

        //Clamp Then Scale Then Cast
        UInt32 NewValue8 = (UInt32)(Math.Min(sValR, 1.0f) * UInt32.MaxValue);
        //Result = 0;

        //Scale Then Clamp Then Cast
        UInt32 NewValue9 = (UInt32)(Math.Min(sValR * UInt32.MaxValue, 4294967167));
        //Result = 4294967040;
0
source

Source: https://habr.com/ru/post/1545626/


All Articles