BigDecimal не сохраняет фактическое значение при возврате из метода Java - PullRequest
0 голосов
/ 30 октября 2018

Я делаю приложение для конвертации валют на Java. Некоторые другие удивительные StackOverflowians дали мне совет прочитать BigDecimal с целью замены double, чтобы исправить любые проблемы с точностью.

У меня есть система двух методов; где он конвертирует из начальной валюты в доллары США, а затем конвертирует значение в долларах США в валюту назначения.

Обратите внимание, мои показатели конверсии сохраняются следующим образом:

// Conversion Rates - START (as of October 30, 2018 @ 3:19 AM)
// Rates obtained from exchange-rates.org

//Convert to United States Dollar rates
private final BigDecimal CAD_TO_USD = new BigDecimal(0.76135);
private final BigDecimal EUR_TO_USD = new BigDecimal(1.1345);
private final BigDecimal YEN_TO_USD = new BigDecimal(0.008853);
// Conversion Rates - END

После того, как я заменил свои двойники соответствующими BigDecimals - я решил проверить это и посмотреть, как все это работает.

Мой класс тестера запускает следующий метод для запуска процесса преобразования.

public BigDecimal convert()
{
    BigDecimal value;

    value = convertToUSD(); //Converts the current currency into USD 
    value = convertFromUSD(value);  //Converts the previous USD currency value into the destination currency

    return value;
}

Когда я ввел свои примерные переменные (которые конвертируют 2,78 иены в канадские доллары), я прошел через процесс и обнаружил, что все работает до тех пор, пока я не верну значение.

Из ранее упомянутого метода, convertToUSD() запускается и кодируется следующим образом

private BigDecimal convertToUSD()
{
    switch (fromCurrency)
    {
        case "USD":
            return fromQuantity.multiply(new BigDecimal(1));

        case "CAD":
            return fromQuantity.multiply(CAD_TO_USD);

        case "EUR":
            return fromQuantity.multiply(EUR_TO_USD);

        case "YEN":
            return fromQuantity.multiply(YEN_TO_USD);
    }

    return new BigDecimal(0);
}

ALl значения передаются правильно, он переходит вниз к правильному регистру ("YEN"), а область переменных показывает, что BigDecimal fromQuantity имеет значение intCompact 278 (что имеет смысл для меня)

enter image description here

Как только точка останова возвращается обратно к методу "convert", она все испортила. Вместо возврата 2.78 * 0.008853 = 0.0246 возвращается -9223372036854775808.

enter image description here

Это приводит к возникновению всех других вычислений и возникновению ошибки.

Я новичок в использовании BigDecimal, поэтому, возможно, я делаю совершенно очевидную ошибку; но я счастлив учиться, поэтому я искал совета у вас, ребята:)

Любая помощь приветствуется.

Ответы [ 2 ]

0 голосов
/ 30 октября 2018

Проблема в том, что вы предполагаете, что intCompact представляет версию вашего числа с плавающей запятой. В некоторых случаях это так, но в большинстве случаев - нет.

Например, я пытался свести вашу проблему к минимуму.

import java.math.BigDecimal;

class Main {
  public static void main(String[] args) {
    final BigDecimal YEN_TO_USD = new BigDecimal(0.008853);
    BigDecimal value = new BigDecimal(2.78);

    value = value.multiply(YEN_TO_USD);
    System.out.println(value);
  }
}

Помещая разрыв в строку с println, я получаю следующее:

break point

Вы видите, что intCompact совпадает с вашим (-9223372036854775808). Но stringCache в этом случае является ожидаемым значением.

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

0 голосов
/ 30 октября 2018

ТЛ; др

Используйте String, а не double литералы.

new BigDecimal( "2.78" )           // Pass "2.78" not 2.78
.multiply(
    new BigDecimal( "0.008853" )   // Pass "0.008853" not 0.008853
)
.toString()

0,02461134

Не передавать типы с плавающей точкой

Смысл класса BigDecimal состоит в том, чтобы избежать присущих неточностей , обнаруженных в технологии с плавающей точкой . Типы с плавающей точкой, такие как float / Float и double / Double, компенсируют точность для скорости выполнения. Напротив, BigDecimal медленный, но точный.

Ваш код:

new BigDecimal( 0.76135 )
new BigDecimal( 1.1345 )
new BigDecimal( 0.008853 )

… передает double примитивный литерал. Во время компиляции введенный вами текст 0.76135 анализируется как число, а именно как double (64-битное значение с плавающей точкой). В этот момент вы ввели неточности, присущие этому типу. Другими словами, double, полученный из 0.76135, может больше не быть точно 0.76135.

Давайте сбросим ваши BigDecimal экземпляры сразу после создания экземпляра.

System.out.println( new BigDecimal( 0.76135 ) );    // Passing a `double` primitive.
System.out.println( new BigDecimal( 1.1345 ) );
System.out.println( new BigDecimal( 0.008853 ) );

0.7613499999999999712230192017159424722194671630859375

1.13450000000000006394884621840901672840118408203125

0.0088529999999999997584154698415659368038177490234375

Итак, создавая double числовые значения, вы вызвали технологию с плавающей запятой и внесли неточности.

Использовать строки

Решение? Работайте со строками, полностью избегая типа double.

Поставьте несколько двойных кавычек вокруг этих входных данных и voilà .

System.out.println( new BigDecimal( "0.76135" ) );  // Passing a `String` object.
System.out.println( new BigDecimal( "1.1345" ) );
System.out.println( new BigDecimal( "0.008853" ) );

0,76135

1,1345

0,008853

* +1073 * Пример

Вы ожидали 2.78 * 0.008853 = 0.0246. Давайте попробуем.

BigDecimal x = new BigDecimal( "2.78" );
BigDecimal y = new BigDecimal( "0.008853" );
BigDecimal z = x.multiply( y );
System.out.println( x + " * " + y + " = " + z );

2,78 * 0,008853 = 0,02461134

Затем вы должны изучить округление и усечение с BigDecimal. Уже много раз освещал переполнение стека.

...