random - IEEE 32 位 float 的映射范围 [1 :2) to some arbitrary [a:b)

标签 random floating-point affinetransform

背景:具有任意端点的统一 PRNG

我有一个快速统一的伪随机数生成器,可以在 [1:2) 范围内创建统一的 float32 数字,即 u : 1 <= u <= 2-eps .不幸的是,将端点 [1:2) 映射到任意范围 [a:b) 的端点在 float 学中并不简单。我想通过简单的仿射计算来精确匹配端点。

正式声明

我想制作一个 IEEE-754 32 位浮点仿射函数 f(x,a,b)对于 1<=x<2完全映射的任意a,b 1 -> anextlower(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) 条件。

我试过更换 1本着 Kahan summation 的精神,使用免费的设计参数来取消 FP 错误. 即与 f(x,c0,c1,c2) = (x-c0)*c1 + c2 一个数学解是c0=1,c1=(b-a),c2=a (上面的简单映射), 但是额外的参数让我可以玩弄常量 c0,c1,c2以匹配端点。我不确定我是否充分理解 Kahan 求和背后的原理以应用它们来确定参数或什至确信存在解决方案。感觉就像我在黑暗中颠簸,其他人可能已经找到了光。

旁白:我可以假设以下内容

  • a < b
  • a 和 b 都远离零,即可以忽略次正规
  • a 和 b 相距足够远(以可表示的 FP 值测量)以减轻非均匀量化并避免退化情况

更新

我正在使用 Chux 答案的修改形式来避免 split 。 虽然我不能 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) 保持不变,则可以重用其他行。

最佳答案

OP 的目标

I want to make an IEEE-754 32 bit floating point affine function f(x,a,b) for 1<=x<2 and arbitrary a,b that exactly maps 1 -> a and nextlower(2) -> nextlower(b)

这与“将 IEEE 32 位 float [1:2) 映射到某些任意 [a:b)”略有不同。


一般情况

x0 映射到 y0,将 x1 映射到 y1 以及各种 x -y 之间:

m = (y1 - y0)/(x1 - x0);
y = m*(x - x0) + y0;

OP 的案例

// x0 = 1.0f;
// x1 = nextafterf(2.0f, 1.0f);
// y0 = a;
// y1 = nextafterf(b, a);

#include <math.h>  // for nextafterf()

float x = random_number_1_to_almost_2();
float m = (nextafterf(b, a) - a)/(nextafterf(2.0f, 1.0f) - 1.0f);
float y = m*(x - 1.0f) + a;

nextafterf(2.0f, 1.0f) - 1.0f, x - 1.0fnextafterf(b, a) 是准确的,不会产生计算错误。
nextafterf(2.0f, 1.0f) - 1.0f 是一个略小于 1.0f 的值。


推荐

在端点具有更好的对称性和数值稳定性的其他重组是可能的。

float x = random_number_1_to_almost_2();
float afactor = nextafterf(2.0f, 1.0f) - x;   // exact
float bfactor = x - 1.0f;                     // exact
float xwidth = nextafterf(2.0f, 1.0f) - 1.0f; // exact
// Do not re-order next line of code, perform 2 divisions
float y = (afactor/xwidth)*a + (bfactor/xwidth)*nextafterf(b, a);

注意 afactor/xwidthbfactor/xwidth 在端点处都正好是 0.0 或 1.0,因此满足“maps 1 -> a and nextlower(2) -> 下一个(b)”。不需要扩展精度。


OP 的 (x-c0)*c1 + c2 在将 (x-c0)*c1 除以 (2.0 - 1.0) 或 1.0(隐含)时出现问题,当它应该除以 nextafterf(2.0f, 1.0f) - 1.0f 时。

关于random - IEEE 32 位 float 的映射范围 [1 :2) to some arbitrary [a:b),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68545138/

相关文章:

javascript - 单击按钮播放随机视频

c# - 将 float 显示到文本框类型 "number"

c++ - 优化数学计算(乘法和求和)

c++ - int 到 float 转换的精度损失

python - 仿射 warp_matrix 里面有什么(如何分解)

java - 多边形在旋转时移动

java - 从单独的数组创建随机元素数组,最多给定长度

oracle - 如何使用 PL/SQL 在 Oracle 中创建具有随机字段数的表?

c++ - 非重复随机数发生器

geometry - 3D 旋转分布的平均值和测量