双精度舍入为单精度:强制上限
Rounding of double precision to single precision: Forcing an upper bound
我正在使用 Mersenne Twister 实现,它为我提供双精度数字。
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/FORTRAN/fortran.html(Tsuyoshi Tada 在 Fortran 77 中的实现,我使用的是 genrand_real2)
但是,为了避免在将具有不同精度的数字相乘时出现警告,我的应用程序需要一个单精度随机数。
所以,我写了一个小函数来在两种数据类型之间进行转换:
function genrand_real()
real genrand_real
real*8 genrand_real2
genrand_real = real(genrand_real2())
return
end
我使用 real 和 real*8 来与我正在处理的代码保持一致。
它在大多数情况下都运行良好(除了事实上我不确定 real() 有多快),但是它改变了我的 RNG 的上限,因为转换将 [0,1) 更改为 [0, 1].在遇到问题之前,我从未想过这一点。
我的问题是,如何以有效的方式确保上限,甚至如何编写类似于 genrand_real2(原始函数)的函数来为我提供单精度实数。我的猜测是我只需要替换除数 4294967296.d0 但我不知道用哪个数字
function genrand_real2()
double precision genrand_real2,r
integer genrand_int32
r=dble(genrand_int32())
if(r.lt.0.d0)r=r+2.d0**32
genrand_real2=r/4294967296.d0
return
end
您发布的函数不会生成随机数,它只会通过除以 2^32(正好是 4294967296)或添加将随机整数(从 genrand_int32()
)限制在区间 [0,1) 内如果 int 为负数,则首先为 2^32。 2^32是一个标准整数所能容纳的值的个数,一半为负,一半为正(大约在正端少1),因此来自函数genrand_int32()
.
假设您有从 -10 到 10 的数字,并且想将它们限制在区间 [0,1] 内。最简单的解决方案是将负数加 20(因此正数保持 0-10,负数变为 10-20),然后除以 20。
这正是函数正在做的,只是用 2^31 而不是 10.
如果您想知道为什么函数的区间是 [0, 1]:
由于数字 0 也需要一个点,并且位表示只能存储 2^32 个数字,因此不能有 2^31 个负数和 2^31 个正数 AND 0。解决方案是省略值 +2^ 31(最高正数)因此 1 被排除在您的间隔之外。
所以要将整个事情简化为单精度:
function genrand_real2()
real genrand_real2,r
integer genrand_int32
r=real(genrand_int32())
if(r.lt.0)r=r+2**32
genrand_real2=r/4294967296
return
end
幻数必须保持不变,因为它们与整数相关,而不是实数。
编辑:
你自己已经说过了,所以我只是对其他人重复一遍:为了可移植性,在技术上使用默认类型而不指定精度不是一个好主意。所以你应该在某处做 sp = selected_real_kind(6, 37)
(sp
用于单精度)然后 real(kind=sp)...
和 2.0_sp
等等。
不过,这更像是一个学术观点。
我正在使用 Mersenne Twister 实现,它为我提供双精度数字。
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/FORTRAN/fortran.html(Tsuyoshi Tada 在 Fortran 77 中的实现,我使用的是 genrand_real2)
但是,为了避免在将具有不同精度的数字相乘时出现警告,我的应用程序需要一个单精度随机数。 所以,我写了一个小函数来在两种数据类型之间进行转换:
function genrand_real()
real genrand_real
real*8 genrand_real2
genrand_real = real(genrand_real2())
return
end
我使用 real 和 real*8 来与我正在处理的代码保持一致。 它在大多数情况下都运行良好(除了事实上我不确定 real() 有多快),但是它改变了我的 RNG 的上限,因为转换将 [0,1) 更改为 [0, 1].在遇到问题之前,我从未想过这一点。
我的问题是,如何以有效的方式确保上限,甚至如何编写类似于 genrand_real2(原始函数)的函数来为我提供单精度实数。我的猜测是我只需要替换除数 4294967296.d0 但我不知道用哪个数字
function genrand_real2()
double precision genrand_real2,r
integer genrand_int32
r=dble(genrand_int32())
if(r.lt.0.d0)r=r+2.d0**32
genrand_real2=r/4294967296.d0
return
end
您发布的函数不会生成随机数,它只会通过除以 2^32(正好是 4294967296)或添加将随机整数(从 genrand_int32()
)限制在区间 [0,1) 内如果 int 为负数,则首先为 2^32。 2^32是一个标准整数所能容纳的值的个数,一半为负,一半为正(大约在正端少1),因此来自函数genrand_int32()
.
假设您有从 -10 到 10 的数字,并且想将它们限制在区间 [0,1] 内。最简单的解决方案是将负数加 20(因此正数保持 0-10,负数变为 10-20),然后除以 20。 这正是函数正在做的,只是用 2^31 而不是 10.
如果您想知道为什么函数的区间是 [0, 1]: 由于数字 0 也需要一个点,并且位表示只能存储 2^32 个数字,因此不能有 2^31 个负数和 2^31 个正数 AND 0。解决方案是省略值 +2^ 31(最高正数)因此 1 被排除在您的间隔之外。
所以要将整个事情简化为单精度:
function genrand_real2()
real genrand_real2,r
integer genrand_int32
r=real(genrand_int32())
if(r.lt.0)r=r+2**32
genrand_real2=r/4294967296
return
end
幻数必须保持不变,因为它们与整数相关,而不是实数。
编辑:
你自己已经说过了,所以我只是对其他人重复一遍:为了可移植性,在技术上使用默认类型而不指定精度不是一个好主意。所以你应该在某处做 sp = selected_real_kind(6, 37)
(sp
用于单精度)然后 real(kind=sp)...
和 2.0_sp
等等。
不过,这更像是一个学术观点。