仅使用数学就可以得到小数部分作为整数

时间:2018-07-21 17:36:37

标签: java math kotlin floating-point

我知道开始处理此问题的最简单方法是:

val floatNumber: Float = 123.456f
val decimalPart = floatNumber - floatNumber.toInt() //This would be 0.456 (I don't care about precision as this is not the main objective of my question)

现在在有笔和​​纸的现实世界中,如果我想将小数部分0.456“转换”为整数,我只需要乘以0.456 * 1000,就可以得到所需的结果,即456 (整数)。

许多提议的解决方案建议将数字拆分为字符串,然后以这种方式提取小数部分,但是我需要以数学方式获得解决方案,而不是使用字符串。

给出一个数字,使用未知的小数位数(转换为字符串并在。或之后计算字符是不可接受的),我需要仅使用数学将其小数部分“提取”为整数。

阅读这样的问题,没有运气:

How to get the decimal part of a float?

How to extract fractional digits of double/BigDecimal

如果有人知道Kotlin语言解决方案,那就太好了。我也将这个问题发布在数学平台上,以防万一。 How do I get whole and fractional parts from double in JSP/Java?

更新: 有没有一种“数学”方式来“计算”一个数字有多少个小数? (很明显,当您转换为字符串并计算字符数时,但是我需要避免使用字符串)这将是一个很重要的原因:十进制(0.456)* 10 *小数位数(3)将产生所需的结果。

更新2 这不是我的用例,但我想它将澄清这个想法: 假设您要计算一个常量(例如PI),并希望返回一个整数,该整数最多为该常量小数部分的 50位数字。常量不必一定是无限的(例如可以为0.5,在这种情况下将返回“ 5”)

4 个答案:

答案 0 :(得分:2)

我只需将小数乘以10(或将小数点移到右边),直到没有小数部分为止:

public static long fractionalDigitsLong(BigDecimal value) {
    BigDecimal fractional = value.remainder(BigDecimal.ONE);
    long digits;
    do {
        fractional = fractional.movePointRight(1);  // or multiply(BigDecimal.TEN)
        digits = fractional.longValue();
    } while (fractional.compareTo(BigDecimal.valueOf(digits)) != 0);
    return digits;
}

注意1:使用BigDecimal可以避免浮点精度问题

注意2:使用compareTo是因为equals也会比较小数位数("0.0"不等于"0.00"

(确保BigDecimal已经知道小数部分的大小,只知道scale()返回的值)


补语

如果使用BigDecimal,则整个问题可以压缩为:

public static BigInteger fractionalDigits(BigDecimal value) {
    return value.remainder(BigDecimal.ONE).stripTrailingZeros().unscaledValue();
}
如果需要,可以抑制

剥离零点

答案 1 :(得分:1)

如果您将某些String转换器与method()配合使用,则不确定在此特定问题上是否对您不利。那是获得正确答案的一种方法。我知道您说过您不能使用String,但是可以在Custom方法中使用Strings吗?这样可以为您提供精确所需的答案。这是可以帮助我们转换数字的类:

class NumConvert{
     String theNum;

     public NumConvert(String theNum) {
           this.theNum = theNum;
}
     public int convert() {
            String a = String.valueOf(theNum);
            String[] b = a.split("\\.");
            String b2 = b[1];
            int zeros = b2.length();
            String num = "1";
            for(int x = 0; x < zeros; x++) {
                num += "0";
    }
            float c = Float.parseFloat(theNum);
            int multiply = Integer.parseInt(num);
            float answer = c - (int)c;
            int integerForm = (int)(answer * multiply);
            return integerForm;
     }
 }

然后在您的主要班级:

public class ChapterOneBasics {
    public static void main(String[] args) throws java.io.IOException{
          NumConvert n = new NumConvert("123.456");
          NumConvert q = new NumConvert("123.45600128");
          System.out.println(q.convert());
          System.out.println(n.convert());
    }
}

输出:

 45600128
 456

答案 2 :(得分:1)

浮点数或双精度数不精确,仅是近似值-不精确。因此12.345在12.3449 ...和12.3450 ...之间。

这意味着不能将 12.340与12.34 区分开。 “小数部分”将是34除以100。 同样, 12.01的“小数部分” 1 除以100, 12.1的“小数部分”也将1 除以10。

因此,一个完整的算法将是(使用Java):

int[] decimalsAndDivider(double x) {
    int decimalPart = 0;
    int divider = 1;
    final double EPS = 0.001;
    for (;;) {
        double error = x - (int)x;
        if (-EPS < error && error < EPS) {
            break;
        }
        x *= 10;
        decimalPart = 10 * decimalPart + ((int)(x + EPS) % 10);
        divider *= 10;
    }
    return new int[] { decimalPart, divider };
}

答案 3 :(得分:0)

我在测试了一段时间后于昨天发布了以下解决方案,后来发现由于浮动,双精度和十进制精度问题,该解决方案并不总是有效。
我的结论是,如果您想要无限的精度,这个问题是无法解决的
因此,我将代码重新发布以供参考:

fun getDecimalCounter(d: Double): Int {
    var temp = d
    var tempInt = Math.floor(d)

    var counter = 0

    while ((temp - tempInt)  > 0.0 ) {
        temp *= 10
        tempInt = Math.floor(temp)
        counter++
    }

    return counter
}


fun main(args: Array <String> ) {
    var d = 3.14159
    if (d < 0) d = -d
    val decimalCounter = getDecimalCounter(d)
    val decimalPart = (d - Math.floor(d))
    var decimalPartInt = Math.round(decimalPart * 10.0.pow(decimalCounter))
    while (decimalPartInt % 10 == 0L) {
        decimalPartInt /= 10
    }
    println(decimalPartInt)
}

由于精度较低,我掉落了浮子,并使用了双打。
由于精度的原因,最后的舍入也是必需的。