Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 可以准确转换为美分(长)的最大美元金额(双倍)_Java_Types_Currency - Fatal编程技术网

Java 可以准确转换为美分(长)的最大美元金额(双倍)

Java 可以准确转换为美分(长)的最大美元金额(双倍),java,types,currency,Java,Types,Currency,我正在编写一个银行程序,其中包含一个变量长余额,用于在帐户中存储美分。当用户输入金额时,我有一种从美元到美分的转换方法: public static long convertFromUsd (double amountUsd) { if(amountUsd <= maxValue || amountUsd >= minValue) { return (long) (amountUsd * 100.0) } else { //no conversio

我正在编写一个银行程序,其中包含一个变量
长余额
,用于在帐户中存储美分。当用户输入金额时,我有一种从美元到美分的转换方法:

public static long convertFromUsd (double amountUsd) {
   if(amountUsd <= maxValue || amountUsd >= minValue) {
      return (long) (amountUsd * 100.0)
   } else {
      //no conversion (throws an exception, but I'm not including that part of the code)
   }
}
公共静态长期转换美元(双倍金额美元){
如果(amountUsd=最小值){
回报(长期)(金额USD*100.0)
}否则{
//无转换(抛出异常,但我不包括代码的这一部分)
}
}
在我的实际代码中,我还检查了
amountUsd
的小数不超过2,以避免无法准确转换的输入(例如20.001美元不完全是2000美分)。对于这个示例代码,假设所有输入都有0、1或2个小数

起初,我查看了
Long.MAX_VALUE
(9223372036854775807美分),并假设
double maxValue=92233720368547758.07
是正确的,但它给了我大量的舍入误差:

convertFromUsd(92233720368547758.07)
输出9223372036854775807

convertFromUsd(92233720368547758.00)
给出相同的输出9223372036854775807


我应该如何设置
double maxValue
double minValue
以始终获得准确的返回值?

您查看了可能的最大长数值,而可能的最大双精度则较小。计算
(amountUsd*100.0)
会产生一个double(之后会被转换为long)


您应该确保
(amountUsd*100.0)
不能大于最大的double,也就是说。

使用double,在Java中最大的是:70368744177663.99

double中的值是64位(8字节),表示:

  • 小数和整数
  • +/-
  • 问题是要使它不是0.99的四舍五入,所以整数部分得到46位,其余部分需要用于小数

    您可以使用以下代码进行测试:

    double biggestPossitiveNumberInDouble = 70368744177663.99;
    
    for(int i=0;i<130;i++){
        System.out.printf("%.2f\n", biggestPossitiveNumberInDouble);
        biggestPossitiveNumberInDouble=biggestPossitiveNumberInDouble-0.01;
    }
    
    在这种情况下,最好的方法是不要解析为double:

    System.out.println("Enter amount:");
    String input = new Scanner(System.in).nextLine();
    
    int indexOfDot = input.indexOf('.');
    if (indexOfDot == -1) indexOfDot = input.length();
    int validInputLength = indexOfDot + 3;
    if (validInputLength > input.length()) validInputLength = input.length();
    String validInput = input.substring(0,validInputLength);
    long amout = Integer.parseInt(validInput.replace(".", ""));
    
    System.out.println("Converted: " + amout);
    
    这样一来,你就不会遇到双重限制,而只会遇到长期限制

    但最终将使用一个为货币制作的数据类型。

    您可以使用
    BigDecimal
    作为临时持有者 如果您有一个非常大的双精度(介于
    double.MAX_VALUE/100.0+1
    double.MAX_VALUE
    之间)计算
    usd*100.0
    将导致双精度溢出

    但是既然你知道,
    
    
  • 通过使用
    BigDecimal
    您根本不必费心指定一个最大值->任何表示美元的给定双精度可以转换为表示美分的长数值(假设您不必处理美分分数)

    双美元=123.45;
    long cents=BigDecimal.valueOf(usd).movePointRight(2).setScale(0).longValueExact();
    
    注意:请记住,double首先无法存储准确的美元信息。通过将双精度转换为
    BigDecimal
    ,无法恢复丢失的信息。
    临时
    BigDecimal
    给您的唯一好处是
    usd*100
    的计算不会溢出。

    首先,对货币金额使用
    double
    是有风险的

    TL;博士 我建议保持在17592186044416美元以下

    数字的浮点表示法(
    double
    type)不使用小数(1/10、1/100、1/1000等),而是使用二进制(例如1/128、1/256)。因此,
    double
    数字永远不会精确地命中像
    $1.99
    这样的值。在大多数情况下,它将被关闭一小部分

    希望从十进制数字输入(“1.99”)到
    双精度
    数字的转换将以最接近的二进制近似值结束,即比精确的十进制值高或低一小部分

    为了能够正确表示从
    $xxx.00
    $xxx.99
    的100个不同的分数值,您需要一个二进制分辨率,其中至少可以表示小数部分的128个不同值,这意味着最低有效位对应于1/128(或更好),这意味着至少7个尾随位必须专用于小数美元

    double
    格式实际上有53位作为尾数。如果分数需要7位,则最多可以将46位用于整数部分,这意味着您必须保持低于2^46美元(
    $70368744177664.00
    ,70万亿)的绝对限制

    作为预防措施,我不太相信将十进制数字转换为双精度的最佳舍入特性,因此我会为小数部分多花两位,结果限制为2^44美元,
    17592186044416美元

    代码警告 您的代码中有一个缺陷:

    return (long) (amountUsd * 100.0);
    
    如果
    double
    值介于两个精确的美分之间,这将向下截断到下一个较低的美分,这意味着例如“123456789.23”可能会变成
    123456789.229…
    作为
    double
    而被截断为
    12345678922
    作为
    long
    的美分

    你最好使用

    return Math.round(amountUsd * 100.0);
    
    这将以最接近的美分值结束,很可能是“正确”的值

    编辑:

    关于“精确性”的评论 你经常读到浮点数不精确的陈述,然后在下一句中,作者主张
    BigDecimal
    或类似的表示法是精确的

    此类语句的有效性取决于要表示的数字类型

    当今计算机中使用的所有数字表示系统对于某些类型的数字都是精确的,而对于其他类型的数字则是不精确的。让我们从数学中提取几个例子,看看它们适合于一些典型的数据类型:

      <
      return Math.round(amountUsd * 100.0);