Python 渐近函数逼近零的浮点问题

Python 渐近函数逼近零的浮点问题,python,math,floating-accuracy,rounding-error,hyperbolic-function,Python,Math,Floating Accuracy,Rounding Error,Hyperbolic Function,来自MATLAB的python新手 我使用的是震级尺度函数的双曲正切截断。 在将0.5*math.tanh(r/rE-r0)+0.5函数应用于范围值数组r=np.arange(0.1100.01,0.01)时,我遇到了问题。我为接近零的一侧的函数获得了几个0.0值,这在我执行对数时会导致域问题: P1 = [ (0.5*m.tanh(x / rE + r0 ) + 0.5) for x in r] # truncation function 我使用以下方法: P1 = [ -m.log10(x

来自MATLAB的python新手

我使用的是震级尺度函数的双曲正切截断。 在将
0.5*math.tanh(r/rE-r0)+0.5
函数应用于范围值数组
r=np.arange(0.1100.01,0.01)
时,我遇到了问题。我为接近零的一侧的函数获得了几个
0.0
值,这在我执行对数时会导致域问题:

P1 = [ (0.5*m.tanh(x / rE + r0 ) + 0.5) for x in r] # truncation function
我使用以下方法:

P1 = [ -m.log10(x) if x!=0.0 else np.inf for x in P1 ]

这对我所做的足够了,不过是一种创可贴解决方案。 根据数学明确性的要求:

在天文学中,震级的工作原理大致如下:

mu = -2.5log(flux) + mzp # apparent magnitude
其中mzp是每秒可以看到1个光子的量级。因此,更大的通量等于更小(或更负)的视震级。我正在为使用多个组件函数的源创建模型。例如,两个sersic函数具有不同的sersic索引,内部组件上有
P1
外部截断,外部组件上有
1-P1
内部截断。这样,当向每个分量添加截断函数时,由半径定义的幅值将变得非常大,因为当
P1
渐近接近零时,mu1-2.5*log(
P1
)变得非常小

TLDR:我想知道的是,是否有一种方法可以保存精度不足以与零区分的浮点(特别是在渐近接近零的函数的结果中)。这一点很重要,因为当取这些数字的对数时,结果是域错误

非对数P1中的输出开始读取零之前的最后一个数字是
5.551115123125783e-17
,这是一个常见的浮点算术舍入误差结果,其中所需结果应为零

如有任何意见,将不胜感激

@用户:丹 没有把我的整个剧本:

xc1,yc1 = 103.5150,102.5461;
Ee1 = 23.6781;
re1 = 10.0728*0.187;
n1 = 4.0234;
# radial brightness profile (magnitudes -- really surface brightness but fine in ex.)
mu1 = [ Ee1 + 2.5/m.log(10)*bn(n1)*((x/re1)**(1.0/n1) - 1) for x in r];

# outer truncation
rb1 = 8.0121
drs1 = 11.4792

P1 = [ (0.5*m.tanh( (2.0 - B(rb1,drs1) ) * x / rb1 + B(rb1,drs1) ) + 0.5) for x in r]

P1 = [ -2.5*m.log10(x) if x!=0.0 else np.inf for x in P1 ] # band-aid for problem

mu1t = [x+y for x,y in zip(P1,mu1)] # m1 truncated by P1 
式中,bn(n1)=7.72和B(rb1,drs1)=2.65-4.98*(r_b1/(-drs1))

mu1是要截断的分量的幅值剖面。P1是截断函数。P1的许多最终条目为零,这是因为由于浮点精度,浮点与零没有区别

查看问题的简单方法:

>>> r = np.arange(0,101,1)
>>> P1 = [0.5*m.tanh(-x)+0.5 for x in r]
>>> P1
[0.5, 0.11920292202211757, 0.01798620996209155, 0.002472623156634768, 0.000335350130466483, 4.539786870244589e-05, 6.144174602207286e-06, 8.315280276560699e-07, 1.1253516207787584e-07, 1.5229979499764568e-08, 2.0611536366565986e-09, 2.789468100949932e-10, 3.775135759553905e-11, 5.109079825871277e-12, 6.914468997365475e-13, 9.35918009759007e-14, 1.2656542480726785e-14, 1.7208456881689926e-15, 2.220446049250313e-16, 5.551115123125783e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

还要注意零之前的浮点数。

回想一下,双曲正切可以表示为(1-e^{-2x})/(1+e^{-2x})。通过一点代数,我们可以得到0.5*tanh(x)-0.5(函数的负数)等于e^{-2x}/(1+e^{-2x})。它的对数将是
-2*x-log(1+exp(-2*x))
,这将在任何地方都有效且稳定

也就是说,我建议您更换:

P1 = [ (0.5*m.tanh( (2.0 - B(rb1,drs1) ) * x / rb1 + B(rb1,drs1) ) + 0.5) for x in r]

P1 = [ -2.5*m.log10(x) if x!=0.0 else np.inf for x in P1 ] # band-aid for problem
使用这种更简单、更稳定的方法:

r = np.arange(0.1,100.01,0.01)
#r and xvals are numpy arrays, so numpy functions can be applied in one step
xvals=(2.0 - B(rb1,drs1) ) * r / rb1 + B(rb1,drs1)
P1=2*xvals+np.log1p(np.exp(-2*xvals))

有两件事你可以试试

(1) 蛮力方法:找到一个可变精度浮点运算包,并使用它代替内置的固定精度。我正在处理Maxima[1]中的问题,我发现为了避免下溢,我必须大大提高浮点精度,但这是可能的。如果你愿意,我可以发布Maxima代码。我可以想象有一些适合Python的变量精度浮点库

(2) 用泰勒级数或其他近似方法近似对数((1/2)(1+tanh(-x)),以避免完全使用对数(tanh(…)


[1]

@GreenAsJade的建议无法解决这个问题。它甚至可能会使问题复杂化。NumPy不是计算机代数系统,喜欢固定精度的数字。@GreenAsJade:他似乎已经在使用NumPy了,带有
np.arange()
。是的,我看过numpy。你是否建议我使用numpy作为math.tanh的替代品?例如,np.tanh?我看过。它给出了相同的结果。你到底想做什么?一种更干净的方法来处理浮点精度不足以区分0.0的情况?还有,你的确切意思是什么“微弱”数字和“震级标度”"?在计算这些数字之后,你能在数学上更明确地说明你对这些数字到底做了什么吗?可能有一个更稳定的数学解决方案,我们看不到,因为我们不知道这些数字的含义或它们的用途。确切地说。我正在寻找一种方法来区分那些浮点数g点精度不足以区分零。为了清晰起见,我将编辑OP。谢谢。对不起,我现在可能对所有编辑都不清楚。您可能已经注意到,我正在查看的函数更像0.5*m.tanh(-x)+0.5(正如我上次编辑时给出的)。问题是,随着x变大,函数趋于零,数字实际上开始读取零。当我想用对数将其添加到震级标度中时,这是不好的。在~1e-17之后,只有零浮点数。我如何在这里获得非零数字(不管有效数字有多小,我只需要为对数定义指数)@user3208266:啊!对不起,在你上次编辑之前我写了大部分内容。请给我一点时间,我会将其修复为渐进大值,而不是渐进小值。我指的是上述函数中的大值
x
。@user3208266:好的,它现在是为大x值设计的。我建议使用
log1p(…)
而不是
log(1 + ...)
。我不认为在这种情况下会有太大的不同,但一般来说,当
较小时会更安全。我从未听说过Maxima,谢谢你的链接。我将继续更严格地寻找Python中的可变精度浮点库。我推荐用于Python的gmpy2:它为va包装了GNU的MPFR库变量精度(二进制)浮点。