将一个数字格式化为另一个数字的格式

Posted

技术标签:

【中文标题】将一个数字格式化为另一个数字的格式【英文标题】:Format a number to the format of another number 【发布时间】:2015-11-25 10:24:26 【问题描述】:

有没有办法使用 Java 的标准库?

说我有一个号码double a = 3.03

我可以使用数字 a 作为格式化模型,以便数字 double b = 56.0934143 通过执行类似 formatNumber(b, a) 的操作格式化为字符串 "56.09"

【问题讨论】:

aString 还是数字类型? 对不起,我会更新问题。 (这是双) 像 3.03 这样的数字没有格式。这只是一个数字。 在内部,所有双精度都是 64 位浮点值,因此它的唯一格式对于所有双精度都是相同的。 @PeterLawrey 是的,但似乎可以根据数字中的信息创建格式 【参考方案1】:

你的问题在某种意义上是可能的,但可能会变得棘手......

挑战源于浮点数的表示方式。见this discussion。查看浮点数,很难确定用户想要的以 10 为底的位数。

例如,如果您用单精度浮点数表示 3.03 并转换回以 10 为底的值,您将得到更接近 3.029999971389771 的值。这是因为 3.03 不能完美地以基数 2 表示。

所以你需要一些复杂的逻辑来确定如果浮点数是3.029999971389771,用户可能想要一个两位小数3.03。某种意义上是可行的。如果没有添加额外的错误,Java 库可以做到这一点。 Excel does 它,但这是一个比你想象的更棘手的问题。


您可能会得到一些最适用于使用 BigDecimal 的其他一些答案的东西。如果您称为a 的数字是一个硬编码常量,它可能会正常工作,但如果不是,那么您很可能偶尔会得到比您想要的多得多的数字。

大十进制方法出错的示例 1

double x1 = 3.1;   
double x2 = Math.acos(Math.cos(x1));   //take the cosine and then inverse
                                       //to introduce a TINY bit more error
                                       //to floating point representation of 3.1
BigDecimal b = BigDecimal.valueOf(x2); 
System.out.println(b);

打印出来的是什么?

3.1000000000000005

当我们可能需要一个 1 位数字时,我们有一个 16 位数字。

浮点不精确的另一个例子

double x1 = 3.03;   
double x2 = 0;
for(int i = 0; i < 100; i++)
    x2 += x1;
System.out.println(x2);

我们将 3.03 添加到自身到 100 次。我们得到 303 吗?不!

302.99999999999966

你想在这里打印多少位?

【讨论】:

在这两种情况下,您都应该真正应用适当的舍入。但是,如果您知道这是什么,则可以将其应用于 b 值,这使得找到一个好的用例相当棘手。 ;) +1【参考方案2】:

我怀疑这并不像你想象的那么有意义,但你可以用 BigDecimal 做到这一点

static String formatNumber(double b, double example) 
    return BigDecimal.valueOf(b).setScale(
                         BigDecimal.valueOf(example).scale(), 
                         RoundingMode.HALF_UP)
                     .toString();

这将给一个数字用于另一个数字的比例。

注意比例

3.03 or 3.0300000 is 2
3 or 3.0 or 3.00000 is 1

【讨论】:

如果每次调用函数时“示例”都是硬编码常量,这可能会正常工作。否则,我会担心浮点错误会导致奇怪的行为。 @MatthewGunn double 的表示错误不是随机错误。它是可预测的和一致的。但是,我同意,如果您传递任何未经舍入计算的值,则比例很可能会达到double 的精度,并且这种方法实际上不会做任何事情。 是的,所以如果他只是使用硬编码常量来确定格式的双 a,我认为他很好。另外,如果他做一些四舍五入,他可能没问题。我认为我应该提出浮点精度问题,因为:(i)几个答案没有提到它,并且(ii)如果一个天真地认为双精度数完美地代表数字,它可能会导致可怕的混乱错误。【参考方案3】:

数字3.03,当它存储在double中时,存储为8字节,64位。这是全部价值:

01000000_00001000_00111101_01110000_10100011_11010111_00001010_00111101

这些位包括数字的尾数、符号和指数。其中没有任何内容表明格式。 8个字节的信息里没有那个位置。

当您使用 System.out.print 打印双精度时,print 方法会根据区分此数字与最接近的双精度数字的能力来决定要打印多少位。因此,print 决定格式 - 它不存储在数字中。

但您可以使用Formatter(例如System.out.printf,它使用Formatter)或DecimalFormat来决定要打印数字的格式。

在这种情况下,您决定格式。它与数字无关。例如,使用

System.out.printf("%7.3f%n",a);

你会得到结果

  3.030

在您的 b 上使用它会导致

 56.093

所以你可以指定格式,但不是由数字决定的。由"%7.3f" 格式决定。

【讨论】:

我明白原始类型double中没有包含任何格式信息!我想知道任何 Java 库中是否有某种功能可以“计算”给定数字的格式并用它格式化另一个数字。这可以通过删除所有尾随零然后计算剩余的零来完成。我现在明白没有这样的功能,所以我必须自己实现它——但它肯定已经完成了。在这种情况下,我会使用它而不是我自己编写的实现。 @Joakim 我认为没有这样的功能的原因是它不是很有用。尽管 Java 格式化数字的决定已记录在案,但它是相当随意的,并且与其他编程语言不同。程序员或用户自己决定他们想要看到的格式会更有用,因此对于这个相当不寻常的要求没有库调用。【参考方案4】:

或者类似的东西:

    String number_1 = String.valueOf(3.03);
    double number_2 = 100.2397;
    String replace=number_1.replaceAll("\\d", "#");
    DecimalFormat df2 = new DecimalFormat(replace);

    double dd2dec = Double.parseDouble(df2.format(number_2));
    System.out.println(dd2dec);

O/P:

100.24

【讨论】:

以上是关于将一个数字格式化为另一个数字的格式的主要内容,如果未能解决你的问题,请参考以下文章

如何仅使用货币代码将数字格式化为货币?

将“数字”字符串格式化为数字

将字符串数字格式化为双变量,小数点后有两个数字

将数字格式化为货币金额

如何将数字格式化为货币字符串

使用 DecimalFormat 进行格式化会引发异常 - “无法将给定对象格式化为数字”