从浮点数恢复原始数字 [英] Recover the original number from a float

查看:177
本文介绍了从浮点数恢复原始数字的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

数字正以浮点/双精度等形式存储在数据库中(不受控制)。



当我将它们拉出时, em> - 例如 0.1 将会出现(格式化时)为 0.100000001490116119384765625



有没有可靠的方法来恢复这些数字?



我试过了 new BigDecimal(((Number)o) .doubleValue()) BigDecimal.valueOf((Number)o).doubleValue())我仍然得到损坏的结果。



我知道我可以假设小数位数和四舍五入,但这将打破故意的数字0.33333333333



有一个简单的方法可以用于大多数理性值?



em>是否有一种简单的方法来找到浮点数的小三角形内的最小有理数?。

解决方案<



我选择了一个 Rational 的.cs.princeton.edu / java / 92symbolic / Rational.java.htmlrel =nofollow>实现



我重构了它使用 long 而不是 int ,然后添加:

  //默认delta应用。 
public static final double DELTA = 0.000001;

public static Rational valueOf(double dbl){
return valueOf(dbl,DELTA);
}

//为所提供的增量中的值创建一个合理的。
public static Rational valueOf(double dbl,double delta){
//主要检查。
if(delta< = 0.0){
throw new IllegalArgumentException(Delta must be> 0.0);
}
//删除整数部分。
long integral =(long)Math.floor(dbl);
dbl - = integral;
//我们正在寻找的值。
final Rational d = new Rational((long)((dbl)/ delta),(long)(1 / delta));
//最小值= d - delta。
final Rational min = new Rational((long)((dbl-delta)/ delta),(long)(1 / delta));
//最大值= d + delta。
final Rational max = new Rational((long)((dbl + delta)/ delta),(long)(1 / delta));
//启动流畅的序列。
Rational l = ZERO;
Rational h = ONE;
Rational found = null;
//保持切片,直到我们到达delta范围内。
do {
//在min和max之间 - >找到了。
if(found == null& min.compareTo(l)< = 0&& max.compareTo(l)> = 0){
found = 1;
}
if(found == null& min.compareTo(h)< = 0&& max.compareTo(h)> = 0){
found = h;
}
if(found == null){
//打开mediant。
Rational m = mediant(l,h);
//用mediant替换l或h。
if(m.compareTo(d)< 0){
l = m;
} else {
h = m;
}
}

} while(found == null);

//返回符号和积分。
if(integral!= 0){
found = found.plus(new Rational(integral,1));
}
//这就是我。
return found;
}

public BigDecimal toBigDecimal(){
//只做4位小数。
return toBigDecimal(4);
}

public BigDecimal toBigDecimal(int digits){
//执行到n个小数位。
return new BigDecimal(num).divide(new BigDecimal(den),digits,RoundingMode.DOWN).stripTrailingZeros();
}

基本上 - 算法以0-1的范围开始。在每次迭代中,我检查是否范围的任一端落在我的d-delta-d + delta范围之间。如果我们找到了答案。



如果没有找到答案,我们会采取 mediant ,并替换其中一个限制。选择要替换的限制,以确保始终围绕 d 的限制。



这实际上是做一个二进制



数学上,我爬下了 Stern-Brocot Tree 选择分支,让我将所需的数字包围起来,直到我进入所需的三角洲。



注意:我没有完成我的测试,但它确实找到 1/10 为我的输入 0.100000001490116119384765625 355/113 <$ c>和 1/3 / code>为π。


Numbers are being stored in a database (out of my control) as floats/doubles etc.

When I pull them out they are damaged - for example 0.1 will come out (when formatted) as 0.100000001490116119384765625.

Is there a reliable way to recover these numbers?

I have tried new BigDecimal(((Number) o).doubleValue()) and BigDecimal.valueOf(((Number) o).doubleValue()) but these do not work. I still get the damaged result.

I am aware that I could make assumptions on the number of decimal places and round them but this will break for numbers that are deliberately 0.33333333333 for example.

Is there a simple method that will work for most rationals?

I suppose I am asking is there a simple way of finding the most minimal rational number that is within a small delta of a float number?.

解决方案

Here's the bludgeon way I have done it - I would welcome a more elegant solution.

I chose an implementation of Rational that had a mediant method ready-made for me.

I refactored it to use long instead of int and then added:

// Default delta to apply.
public static final double DELTA = 0.000001;

public static Rational valueOf(double dbl) {
  return valueOf(dbl, DELTA);
}

// Create a good rational for the value within the delta supplied.
public static Rational valueOf(double dbl, double delta) {
    // Primary checks.
    if ( delta <= 0.0 ) {
        throw new IllegalArgumentException("Delta must be > 0.0");
    }
    // Remove the integral part.
    long integral = (long) Math.floor(dbl);
    dbl -= integral;
    // The value we are looking for.
    final Rational d = new Rational((long) ((dbl) / delta), (long) (1 / delta));
    // Min value = d - delta.
    final Rational min = new Rational((long) ((dbl - delta) / delta), (long) (1 / delta));
    // Max value = d + delta.
    final Rational max = new Rational((long) ((dbl + delta) / delta), (long) (1 / delta));
    // Start the fairey sequence.
    Rational l = ZERO;
    Rational h = ONE;
    Rational found = null;
    // Keep slicing until we arrive within the delta range.
    do {
        // Either between min and max -> found it.
        if (found == null && min.compareTo(l) <= 0 && max.compareTo(l) >= 0) {
            found = l;
        }
        if (found == null && min.compareTo(h) <= 0 && max.compareTo(h) >= 0) {
            found = h;
        }
        if (found == null) {
            // Make the mediant.
            Rational m = mediant(l, h);
            // Replace either l or h with mediant.
            if (m.compareTo(d) < 0) {
                l = m;
            } else {
                h = m;
            }
        }

    } while (found == null);

    // Bring back the sign and the integral.
    if (integral != 0) {
        found = found.plus(new Rational(integral, 1));
    }
    // That's me.
    return found;
}    

public BigDecimal toBigDecimal() {
  // Do it to just 4 decimal places.
  return toBigDecimal(4);
}

public BigDecimal toBigDecimal(int digits) {
  // Do it to n decimal places.
  return new BigDecimal(num).divide(new BigDecimal(den), digits, RoundingMode.DOWN).stripTrailingZeros();
}

Essentially - the algorithm starts with a range of 0-1. At each iteration I check to see if either end of the range falls between my d-delta - d+delta range. If it does we've found an answer.

If no answer is found we take the mediant of the two limits and replace one of the limits with it. The limit to replace is chosen to ensure the limits surround d at all times.

This is essentially doing a binary-chop search between 0 and 1 to find the first rational that falls within the desired range.

Mathematically I climb down the Stern-Brocot Tree choosing the branch that keeps me enclosing the desired number until I fall into the desired delta.

NB: I have not finished my testing but it certainly finds 1/10 for my input of 0.100000001490116119384765625 and 1/3 for 1.0/3.0 and the classic 355/113 for π.

这篇关于从浮点数恢复原始数字的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆