Java中的货币转换器具有精度和浓缩问题

Posted

技术标签:

【中文标题】Java中的货币转换器具有精度和浓缩问题【英文标题】:Currency Converter in Java having precision and condensing issues 【发布时间】:2018-10-26 01:58:08 【问题描述】:

我得到了一个用 Java 编写的作业。

我们将开发一个货币转换应用程序,该应用程序询问他们要转换的货币、所述货币的数量以及他们希望转换成的货币。

该指令指出我们应该包括 4 种货币来转换/转换。

我很早就完成了作业,我去给我的教授看,她注意到了一些问题。她喜欢代码的组织性和清晰性,但她认为我可以将其缩小一点,并解决我在小数精度方面遇到的问题。

关于缩短它的时间,她的主要论点是我有 7 个保持汇率的常数。我对此感到相当自豪,因为对于每种可能的组合,7 个费率比 12 个单独的费率要短得多。代码如下。

// Conversion Rates - START (as of October 14, 2018 @ 1:03 AM)
// Rates obtained from exchange-rates.org
private final double USD_TO_USD = 1;

//Convert to United States Dollars
private final double CAD_TO_USD = 0.76792;
private final double EUR_TO_USD = 1.1407;
private final double YEN_TO_USD = 0.008923;

//Convert from United States Dollars
private final double USD_TO_CAD = 1.3022;
private final double USD_TO_EUR = 0.87662;
private final double USD_TO_YEN = 112.06;
// Conversion Rates - END

我的方法背后的思考过程是将所有内容转换为美元,然后从美元转换为目标货币。我不确定如何进一步浓缩这一点,所以这是我遇到的问题之一。

我遇到的主要问题是精度。由于该课程主要基于商业数学,因此这些程序通常用于转换数百万/数十亿的货币。我知道 double 已经有自己的精度继承问题,但我不确定要使用哪些其他数据类型。我遇到过 BigDecimal,我会在发布后研究一下。

据我了解,汇率的小数位数会直接影响结果的精度。汇率右侧的数字越多;更好。我是否应该养成包含大量小数点的习惯,以使出现精度问题变得非常困难?或者使用 BigDecimal 通常会解决问题吗?

我考虑过放弃使用 double 并改用 int;所以不是双重持有 5.00,而是持有 500 的整数;但在这一点上,我不确定“正确”的继续方式。

所以我来问你们好人:D

我很乐意尽可能多地学习,因此我们非常感谢您提供任何帮助。

谢谢

更新: 我花了一些时间检查了 BigDecimal,我得到了它的工作;除了我现在偏离的数量从 1 到 10 不等(在非常大的数字中可能更多,但我没有用不同的数字测试超过 5 次)。

在我的测试中,我将 98765432.00 日元兑换成美元,汇率为 1 日元 = 0.008907 美元。根据我当时用来验证的网站 - 以美元计算的结果应该是 879,701.24 美元;但我的程序获得了 879,703.70 美元。甚至我的科学计算器也得到了和我的 java 程序一样的东西。

【问题讨论】:

要么使用BigDecimal,要么将所有内容转换为longint 可能不足以保存乘法结果 BigDecimal。绝对是 BigDecimal。永远不要使用浮点数来表示货币。 您不需要有最后 3 次的转化率。 4费就够了。如果说您想从 CAD 转换为 EUR 只需 CAD$ * (CAD_TO_USD / EUR_TO_USD); 也不需要标识常量。你只需要 3 个常量。 @RaymondTey,我从来不知道你能做到这一点。将欧元转换为日元是否也遵循 YEN = EUR * (EUR_TO_USD/YEN_TO_USD)? 【参考方案1】:

只需继续使用 BigDecimal 和 BigDecimal 一样实现您的方法,您不会丢失任何精度,但使用 double 时,您在处理更大的数字时可能会丢失。

请通过以下 *** 链接了解有关 BigDecimal 的更多信息: Double vs. BigDecimal?

你在正确的轨道上,继续摇摆和快乐学习。

【讨论】:

嗨,我花了一些时间检查了 BigDecimal,我得到了它的工作;除了我现在偏离的数量从 1 到 10 不等(在非常大的数字中可能更多,但我没有用不同的数字测试超过 5 次)。在我的测试中,我将 98765432.00 日元兑换成美元,汇率为 1 日元 = 0.008907 美元。根据我当时用来验证的网站 - 以美元计算的结果应该是 879,701.24 美元;但我的程序获得了 879,703.70 美元。甚至我的科学计算器也得到了和我的 java 程序一样的东西。【参考方案2】:

这是另一个实现:

给定这样的货币汇率列表:

USD/GBP => 0.75 
GBP/AUD => 1.7 
AUD/JPY => 90 
GBP/JPY => 150 
JPY/INR => 0.6

Write a method

double convert(String sourceCurrency, double amount, String destCurrency);

==============

package test;

import java.util.*;

public class CurrencyConvertor 
    private Map<String, Map<String, Double>> mapping = new HashMap<String, Map<String, Double>>();
    private boolean bInit = false;
    private boolean bFound = false;

    double convert(String src, double amount, String dst) throws Exception 
        if (src.equals(dst))
            return amount;

        if (!bInit) 
            init();
            bInit = true;
        

        bFound = false;
        if (mapping.get(src) == null) 
            throw new Exception("Invalid conversion");
        

        List<String> visited = new ArrayList<String>();
        visited.add(src);
        double d1 = getRate(visited, src, dst, 1);
        if (bFound)
            return d1 * amount;
        throw new Exception("No mapping invalid conversion");
    

    private double getRate(List<String> visited, String src, String dst, double rate) throws Exception 
        if (bFound == true) 
            return rate;
        

        if (mapping.get(src).get(dst) != null) 
            bFound = true;
            return rate * mapping.get(src).get(dst);
        

        double origRate = rate;
        for (String sInt : mapping.get(src).keySet()) 
            if (visited.contains(sInt)) 
                continue;
            
            visited.add(sInt);
            rate = getRate(visited, sInt, dst, rate * mapping.get(src).get(sInt));
            if (bFound == true) 
                return rate;
            
            visited.remove(sInt);
            rate = origRate;
        

        return origRate;
    

    private void init() 
        // Invalid case data, EUR to INR
        insert("EUR", "USD", 1.2);
        insert("USD", "EUR", 0.75);
        insert("YEN", "INR", 1.2);
        insert("INR", "YEN", 0.75);

        // Valid case data, EUR to INR
        // insert("EUR", "USD", 1.2);
        // insert("USD", "GBP", 0.75);
        // insert("GBP", "AUD", 1.7);
        // insert("AUD", "JPY", 90);
        // insert("GBP", "JPY", 150);
        // insert("JPY", "INR", 0.6);
        //
        // insert("USD", "EUR", 1.0/1.2);
        // insert("GBP", "USD", 1.0/0.75);
        // insert("AUD", "GBP", 1.0/1.7);
        // insert("JPY", "AUD", 1.0/90);
        // insert("JPY", "GBP", 1.0/150);
        // insert("INR", "JPY", 1.0/0.6);
    

    private void insert(String src, String dst, double rate) 
        if (mapping.get(src) == null) 
            Map<String, Double> map = new HashMap<String, Double>();
            map.put(dst, rate);
            mapping.put(src, map);
         else if (mapping.get(src).get(dst) == null) 
            mapping.get(src).put(dst, rate);
        
    

    public static void main(String args[]) 
        try 
            double d = new CurrencyConvertor().convert("EUR", 100, "INR");
         catch (Exception e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
        
    

【讨论】:

以上是关于Java中的货币转换器具有精度和浓缩问题的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Javascript 将货币字符串转换为双精度?

Java从字符串中获取数字或货币格式[关闭]

Java货币转换器应用程序中的异常处理

如何将具有 Decimal 的 spark DataFrame 转换为具有相同精度的 BigDecimal 的 Dataset?

关于java中的short和int类型转换的问题

如何在 .NET 中将双精度值转换为具有固定小数位数的小数类型值