У меня есть очень раздражающая задача, и я застрял с ней. Итак: мне нужно написать функцию, которая возвращает значение числа с плавающей запятой после запятой. Например: параметр будет: 5.456-> и возвращаемое значение должно быть: 456.

Я не могу использовать String (конечно, так было бы проще).

Есть ли у вас какие-либо предложения?

0
Ballazx 18 Янв 2022 в 16:28
Предполагая, что ввод представляет собой BigDecimal input: значение после десятичной точки равно input.remainder(BigDecimal.ONE).unscaledValue() (отрицательные числа требуют особой осторожности)
 – 
user16320675
18 Янв 2022 в 16:38

3 ответа

Для этого требуется несколько шагов с такими примитивами, как float или double. Если бы вам было разрешено использовать BigDecimal, это была бы всего одна строка кода.

Учитывая double d = 5.456;

  • во-первых, отрежьте часть перед плавающей запятой.
    • сделайте это до int full = (int)d;, что будет равно 5
    • полное вычитание из него: d-full теперь будет только частью после точки, поэтому .456
  • теперь используйте цикл, чтобы умножить значение на 10, пока часть «после точки» не станет 0.

Особенность здесь в том, что когда вы используете double, у вас возникают проблемы с точностью с плавающей запятой. Это означает, что d будет иметь значение 0.4560000000000004 между ними. Чтобы решить эту проблему, давайте введем эпсилон.

Полный код выглядит так:

private static final double EPS = 1e-5;
public static void main(String[] args) {
    double d = 5.456;
    System.out.println(afterDot(d));
}

private static int afterDot(double d) {
    d = getDecimals(d);
    while(getDecimals(d) > EPS){ //decimals will likely never be 0 because of precision, so compare with a really small EPS instead of 0
        d *= 10;
    }
    //cast the result to an int to cut off the double precision issues
    return (int)d;
}

private static double getDecimals(double d) {
    int full = (int) d;
    d = d-full;
    return d;
}

Это печатает 456. Я очень уверен, что это можно как-то оптимизировать, но это рабочий первый набросок.

1
f1sh 18 Янв 2022 в 17:04

Вам нужен остаток, умноженный на 10, пока остаток не станет равным 0. Использование BigDecimal для предотвращения проблем с округлением выглядит следующим образом:

final BigDecimal input = new BigDecimal("5.456");
BigDecimal x = input.remainder(BigDecimal.ONE);
while (x.remainder(BigDecimal.ONE).compareTo(BigDecimal.ZERO) > 0) {
    x = x.multiply(new BigDecimal(10));
}
System.out.println(x.longValue());
0
Karsten Gabriel 18 Янв 2022 в 16:56
Почему бы просто не использовать unscaledValue() вместо умножения... у немасштабированного значения есть одно преимущество (возможно, оно не нужно/желательно) - если ввод был "1.230", немасштабированное значение было бы 230, на умножение вы получите только 23)
 – 
user16320675
18 Янв 2022 в 16:57
Потому что некоторые люди просто сосредотачиваются на стоящей перед ними проблеме, а не пытаются решить аналогичные проблемы (разные исходные данные) с помощью абстрактного решения.
 – 
hfontanez
18 Янв 2022 в 17:01
Я не думаю, что BigDecimal здесь вообще "разрешено", так как он просто предоставляет все необходимые методы. Что полностью противоречит цели задачи, которая, как я полагаю, является домашним заданием.
 – 
f1sh
18 Янв 2022 в 17:03
Да, вы правы, unscaledValue будет короче. Первая строка - это просто ввод, я бы предположил, что там можно использовать String. Если вместо этого использовать new BigDecimal(5.456), мы получим проблемы с округлением, и unscaledValue выведет 45600000000000040500935938325710594654083251953125...
 – 
Karsten Gabriel
18 Янв 2022 в 17:05

Вот два способа, которые не учитывают аномалии с плавающей запятой.

double s = 5.456;
System.out.println(s%1);

Или

System.out.println(s-(int)s);

Обе печати

0.4560000000000004

Или используйте BigDecimal и отрегулируйте масштаб и вычтите целочисленное значение.

System.out.println(BigDecimal.valueOf(s)
      .setScale(3)
      .subtract(BigDecimal.valueOf((int)s)));

Отпечатки

.456
0
WJS 18 Янв 2022 в 18:32