4

我有一个float x应该在 <0,1> 范围内的数字,但它经历了几次数值运算 - 结果可能稍微超出 <0,1>。

我需要将此结果转换为uint y使用整个范围的UInt32. 当然,我需要钳制x在 <0,1> 范围内并对其进行缩放。

但是哪种操作顺序更好?

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

或者

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

换句话说,最好先缩放,然后钳或钳,然后缩放?我对 IEEE 浮点表示不是很深,但我相信上述表达式的计算顺序是有区别的。

4

4 回答 4

3

因为从 [0.0f .. 1.0f] 到 [0 .. UInt32.MaxValue] 的乘法本身可以是近似的,所以最明显具有您想要的属性的操作顺序是乘法,然后是钳位,然后是舍入。

要钳制到的最大值是 2 32正下方的浮点数,即4294967040.0f. 虽然这个数字比 UInt32.MaxValue 低几个单位,但允许任何更大的值都意味着转换为UInt32.

下面的任何一行都应该工作:

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

在第一个版本中,您可以选择乘以UInt32.MaxValue。选择是在总体上获得稍微大一点的结果(因此将接近 1.0f 但低于它的更多值四舍五入到 4294967040),或者只将 1.0f 及以上的值发送到 4294967040。


如果之后不乘以太大的数字,您也可以钳制到 [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)

这个计算被认为是一个函数 from xtoy正在增加(包括大约 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 = ...
}
于 2014-06-23T09:23:59.040 回答
2

正确颜色格式转换的三个基本属性是:

  • 黑色必须映射到黑色,白色必须映射到白色(在这种情况下意味着 0.0 —> 0 和 1.0 —> 2^32-1)
  • 映射到目标格式中每个值的源格式中的间隔必须具有尽可能相等的宽度。
  • 均匀分布的输入应映射到目标格式中尽可能均匀分布的输出。

第二点的推论是使用圆形的颜色格式转换几乎总是不正确的,因为映射到最小和最大结果的 bin 通常太小了一半。这对于像 uint32 这样的高精度格式并不那么重要,但正确处理仍然很好。

您在评论中提到您的 C# 代码正在被翻译成 OpenCL。OpenCL 拥有迄今为止我遇到的所有语言中最好的转换集(说真的,如果你正在设计一种面向计算的语言并且你没有复制 OpenCL 在这里所做的,那么你做错了),这使得这很容易:

convert_uint_sat(x * 0x1.0p32f)

但是,您的问题实际上是关于 C#;我不是 C# 程序员,但那里的方法应该是这样的:

if (x <= 0.0F) y = UInt32.MinValue;
else if (x >= 1.0F) y = UInt32.MaxValue;
else y = (uint)Math.Truncate(x * 4294967296.0F);
于 2014-06-25T11:16:03.620 回答
0

由于UInt32-valuespace 中的钳位问题,第二种方法x可能稍微超出第一种方法,即 UInt32 中的每个数字都是有效的。[0,1]第一个也比较容易理解,即得到一个区间和尺度的数字。

IE:

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

此外,我用数百万个值对其进行了测试,它们给出了相同的结果。你使用哪一个并不重要。

于 2014-06-23T07:28:37.957 回答
0

Single 无法支持足够的精度来保持中间结果,因此您需要先缩放然后钳制,但您不能钳制到 UInt32.MaxValue,因为它不能由 single 表示。您可以安全钳制到的最大 UInt32 是4294967167

从这里的代码

        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;
        }

看看这个测试...

        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;
于 2014-06-23T07:48:16.383 回答