【发布时间】:2021-10-03 07:03:32
【问题描述】:
背景故事:具有任意端点的统一 PRNG
我有一个快速统一的伪随机数生成器,可以在 [1:2) 范围内创建统一的 float32 数字,即u : 1 <= u <= 2-eps。不幸的是,将端点 [1:2) 映射到任意范围 [a:b) 的端点在浮点数学中并非易事。我想通过简单的仿射计算来精确匹配端点。
正式声明
我想为1<=x<2 和任意a、b 创建一个IEEE-754 32 位浮点仿射函数f(x,a,b),正好 映射
1 -> a 和 nextlower(2) -> nextlower(b)
其中nextlower(q) 是下一个较低的 FP 可表示数字(例如,在 C++ 中 std::nextafter(float(q),float(q-1)))
我尝试过的
简单映射f(x,a,b) = (x-1)*(b-a) + a 始终满足 f(1) 条件,但有时由于浮点舍入而无法满足 f(2) 条件。
本着Kahan summation 的精神,我尝试用免费的设计参数替换1 以消除FP 错误。
即与
f(x,c0,c1,c2) = (x-c0)*c1 + c2
一个数学解决方案是c0=1,c1=(b-a),c2=a(上面的简单映射),
但是额外的参数让我可以使用常量c0,c1,c2 来匹配端点。我不确定我是否充分理解 Kahan 求和背后的原理,以应用它们来确定参数,甚至确信存在解决方案。感觉就像我在黑暗中颠簸,其他人可能已经找到了光明。
除此之外:假设以下情况我很好
- a
- a 和 b 都远非零,即可以忽略次正规
- a 和 b 相距足够远(以可表示的 FP 值衡量)以减轻非均匀量化并避免退化情况
更新
我正在使用 Chux 答案的修改形式来避免分裂。 虽然我不能 100% 确定我的重构保留了所有魔力,但它仍然适用于我的所有测试用例。
float lerp12(float x,float a,float b)
{
const float scale = 1.0000001f;
// scale = 1/(nextlower(2) - 1);
const float ascale = a*scale;
const float bscale = nextlower(b)*scale;
return (nextlower(2) - x)*ascale + (x - 1.0f)*bscale;
}
请注意,只有最后一行 (5 FLOPS) 取决于 x,因此如果 (a,b) 保持不变,则可以重用其他行。
【问题讨论】:
-
[1...2) 有 N 个浮点成员。 [a...b) 有 M 个成员。将 N 映射到 M 或者稍微不均匀,或者某些 N 成员被忽略,需要重新调用
PSRN()。与此相关的编码目标是什么? -
是的,这两个范围内通常会有不同数量的可表示值。这会导致一些不均匀性。但我相信影响很小。我最关心的是匹配端点。
-
当 a 和 b 靠得很近时,nextlower(2) -> nextlower(b) 在舍入到最近模式下不会自然地退出任何浮点计算(作为一个极端例如,考虑 a = nextlower (b))。您是否尝试过定向舍入模式?
-
@njuffa 我不太关心映射到数量如此之少的范围。重新采用不同的舍入模式:我不反对将其作为解决方案的一部分。
-
Mark,“将 [1:2) 映射到任意 [a:b)”和“1 -> a 和 nextlower(2) -> nextlower(b)”是矛盾的。你想要哪一个?假设这些相同是“有时不满足 f(2) 条件”问题的症结所在。这不是真正的 FP 舍入问题。
标签: random floating-point affinetransform