我有一个数字 float x
,它应该在 <0,1> 范围内,但它经过了几次数值运算 - 结果可能略微超出 <0,1>。
我需要使用 UInt32
的整个范围将此结果转换为 uint y
。当然,我需要将 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 浮点表示不是很了解,但我相信上述表达式的计算顺序有所不同。
最佳答案
因为从 [0.0f .. 1.0f] 到 [0 .. UInt32.MaxValue] 的乘法本身可以是近似的,所以最明显具有您想要的属性的操作顺序是乘法,然后是钳位,然后圆。
要限制的最大值是紧接在 232 以下的 float ,即 4294967040.0f
。尽管此数字比 UInt32.MaxValue 低几个单位,但允许任何更大的值将意味着溢出到 UInt32
的转换。
以下任何一行都应该有效:
y = (uint)round(min(max(x * 4294967040.0F, 0.0F), 4294967040.0F))
在第一个版本中,您可以选择乘以 UInt32.MaxValue
。选择是在总体结果略大一些(因此将接近 1.0f 但低于 1.0f 的值舍入到 4294967040)之间,或者只向 4294967040 发送 1.0f 及以上的值。
您也可以限制在 [0.0f .. 1.0f] 如果您之后没有乘以太大的数字,这样就没有使值大于最大 float 的风险可以转换:
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 = ...
}
关于c# - 将 float 转换为 UInt32 - 哪个表达式更精确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24360347/