在Java中会导致NaN输出的所有情况是什么?

huangapple go评论93阅读模式
英文:

What are all the cases that can result in a NaN output?(in Java)

问题

我正在尝试创建一个生成正态分布随机数的随机数生成器,我有一个用于计算任何给定x的累积分布函数(CDF)的方法,然后我有一个使用牛顿法进行30次迭代的方法,接受一个期望值并找到x,使得CDF(x)=desired。

这是我实现的CDF估计公式

这是我使用的牛顿方法公式(在求导数{\Phi '(x)}时简化并可以化简为分子)

public double cdf(double x){
    double out=0;
    for(int k=0;k<30;k++){
        out+=Math.pow(-1,k)*Math.pow(x,(2*k)+1)/(Math.pow(2,k)*factorial(k)*((2*k)+1));
    }
    return 0.5+(out/Math.sqrt(2*Math.PI));
}
public double invNorm(double desired){
    double x=0;
    double dx;
    for(int i=0;i<30;i++){
        dx=(cdf(x)-desired)*Math.exp(x*x*0.5)*Math.sqrt(2*Math.PI);
        x-=dx;
    } 
    return x;
}

但这段代码有10%的几率返回NaN。当我运行代码创建一个包含这行代码的100个随机数数组,并包含这行代码 "while(Double.isNaN(arr[i])) arr[i]=Misc.invNorm(Math.random());" 以重新分配arr[i],如果它被计算为NaN,那么代码运行完美,并且在第一个标准差之间给出了70%的值,在第二个标准差之间给出了97%,在第三个标准差之间给出了100%的值。cdf函数明显不是问题,因为我通过打印一个具有随机数CDF的数组来检查cdf函数,其中没有一个是NaN,但invNorm没有任何除法,所有幂都是整数,没有三角函数。我考虑了Math.exp返回正无穷大和(cdf(x)-desired)返回0的可能性,但e^450不是正无穷大,而cdf(x)等于期望值非常罕见。

当我们谈论递归函数的实现时,能否有人帮助我?我尝试像这样实现它,其中cdf_n是第n次近似,我考虑x0=0,因此CDF(x0)=0.5,但这几乎60%的时间都会返回NaN。

public double cdf_n(double x,int n){
    if(n==0){
        return 0.5;
    }
    else if(n==1){
        return 1/Math.sqrt(2*Math.PI);
    }
    else{
        return (2-n)*cdf_n(x, n-2);
    }
}
public double cdf(double x){
    int n=0;
    double out;
    double cdf=0;
    do{
        out=cdf_n(x,n)*Math.pow(x,n)/factorial(n);
        cdf+=out;
        n++;
    } while(Math.abs(out)>Math.pow(10.0,-15.0));
    return cdf;
}  

这是递归公式

英文:

I am trying to make a random number generator that generates normally distributed numbers, I have a method CDF to calculate the CDF for any given x, then I have a method invNorm that uses Newton's method over 30 iterations that accepts a desired value and it finds x such that CDF(x)=desired.

This is the formula for estimation of CDF that ive implemented

This is the Newton's method formula I have used(on evaluation the denominator {\Phi '(x)} simplifies and can brought to the numerator

public double cdf(double x){
        double out=0;
        for(int k=0;k<30;k++){
            out+=Math.pow(-1,k)*Math.pow(x,(2*k)+1)/(Math.pow(2,k)*factorial(k)*((2*k)+1));
        }
        return 0.5+(out/Math.sqrt(2*Math.PI));
    }
    public double invNorm(double desired){
        double x=0;
        double dx;
        for(int i=0;i<30;i++){
            dx=(cdf(x)-desired)*Math.exp(x*x*0.5)*Math.sqrt(2*Math.PI);
            x-=dx;
        } 
        return x;
    }

But this code is giving a NaN 10% of the time. When I ran the code to create a array of 100 random numbers and including this line "while(Double.isNaN(arr[i])) arr[i]=Misc.invNorm(Math.random());" that reassigns arr[i] if it got evaluated to NaN, the code ran perfectly and gave 70% value between the first standard deviations, 97 between the second and 100 between the third.The cdf function is definitely not the problem as I checked the cdf function by printing an array of 100 with cdf of random numbers and not a single one of them was NaN, but invNorm does not have any division at all and all powers are integers and there are no trigonometric functions. I considered the possibility of Math.exp returning positive infinity and (cdf(x)-desired) returning 0,but e^450 is not positive infinity and cdf(x) being equal to the desired value is very rare.

While we are on the topic could someone also help me with the implementation of the recursive function for CDF? I tried implementing it like this, where cdf_n is the nth approximation, i consider x0=0, so CDF(x0)=0.5, but this gave NaN almost 60% of the time.

public double cdf_n(double x,int n){
        if(n==0){
            return 0.5;
        }
        else if(n==1){
            return 1/Math.sqrt(2*Math.PI);
        }
        else{
            return (2-n)*cdf_n(x, n-2);
        }
    }
    public double cdf(double x){
        int n=0;
        double out;
        double cdf=0;
        do{
            out=cdf_n(x,n)*Math.pow(x,n)/factorial(n);
            cdf+=out;
            n++;
        } while(Math.abs(out)>Math.pow(10.0,-15.0));
        return cdf;
    }  

This is the recursive formula

答案1

得分: 2

  1. 除零被零除:当你将零除以零时,结果是NaN。

  2. 非零数被零除:将非零数除以零也会得到NaN。

  3. 包含NaN的运算:任何包含NaN作为操作数的算术运算都将产生NaN作为结果。

  4. 具有未定义值的数学函数:某些数学函数对于某些输入可能具有未定义的值,导致NaN。

此外,重要的是要注意,使用==运算符将值与NaN进行比较将始终返回false。而应使用例如Double.isNaN()方法来检查一个值是否为NaN。

英文:

Possible Cases:

  1. Division of zero by zero: When you divide zero by zero, the result is NaN.

  2. Division of a non-zero number by zero: Dividing a non-zero number by zero also results in NaN.

  3. Operations involving NaN: Any arithmetic operation involving NaN as an operand will produce NaN as the result.

  4. Mathematical functions with undefined values: Some mathematical functions may have undefined values for certain inputs, resulting in NaN.

Also it's important to note that comparing a value with NaN using the == operator will always return false. Instead, use the e.g. Double.isNaN() method to check if a value is NaN.

huangapple
  • 本文由 发表于 2023年7月27日 21:21:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76780184.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定