确保十进制数的字符串 repr 总是 n 个字符长
Making sure a string repr of decimal number is always n characters long
我需要确保所有十进制数字的长度始终最多为 15 个字符(包括点),同时尽可能保持精度。因此,它最多必须是 15 个字符,包括“.”。 "E" 用于科学记数法和“-”。
我想的是大数用科学记数法,小数用四舍五入。
例如,对于 1234567891234567.123456789,我会使用科学记数法,但对于 0.123456789123456789,我会四舍五入。
我寻找 java 可以为我执行此操作的库,但我找不到任何可以让我指定表示的字符总数的库。
感谢任何建议或指点。
编辑:更多想法 - 使用 24000E-18 可以毫无损失地表示诸如 0.000000000000024 之类的数字。虽然例如 0.123456789123424 必须遭受一些损失,这当然会使编写任何类型的简单分支算法变得更加困难。
EDIT2:我们用于传输数据的格式是字母数字,并且将数据总共限制为 15 个字符。我必须编写代码来满足格式,以便可以在不触发格式错误的情况下传输数据,同时仍将最大精度保持在限制范围内。
EDIT3:我正在使用它来测试我的功能,但到目前为止,在某些情况下一切都失败了:
Random rnd = new Random();
double n = 100000 + rnd.nextDouble() * 900000;
String res;
double rangeMin = -123456789123456D;
double rangeMax = 123456789123456D;
String val;
for (int i=1;i<=1000;i++) {
n = rangeMin + (rangeMax - rangeMin) * rnd.nextDouble();
val = Double.toString(n);
res = shorteningFunction(val);
System.out.println(val + " " + val.length() + " " + res + " " + res.length());
}
我不想发布不起作用的代码,所以我删除了混乱的代码。
这是伪代码中的思想:
- 将其转换为字符串。
- 检查长度
- 循环:
- 如果长度太长:
- 删除预测。
- 将其转换为字符串。
- 检查长度。
- Return
可以利用BigDecimal
,对于整数部分BigInteger
。
/**
* @param num number representation.
* @param max the maximal length the result should have.
* @return
*/
public static String truncateNumber(String num, int max) {
num = num.replaceFirst("\.0*$", "");
BigDecimal x = new BigDecimal(num);
// Large numbers - integral part
String bigI = x.toBigInteger().toString();
if (bigI.length() > max) {
int expon10 = bigI.length() - max - 1; // - 1 for E
// Digits after E:
if (expon10 == 0) {
++expon10;
} else {
for (int p = expon10; p > 0; ++p) {
++expon10;
p /= 10;
}
}
x = x.movePointLeft(expon10);
String plain = x.toPlainString().substring(0, max - 1 - expon10);
return plain + "E" + expon10;
}
// Tiny numbers - 0.000 (as E-1 already requires 3 positions)
String reprP = x.toPlainString();
if (reprP.startsWith("-0.00")) {
return truncateNumber(num.substring(1), max - 1);
} else if (reprP.startsWith("0.00")) {
String reprE = x.toEngineeringString(); // Does most work.
int epos = reprE.indexOf('E');
String mantissa = reprE.substring(0, epos);
String exp = reprE.substring(epos);
return mantissa.substring(0, Math.min(epos, max - exp.length())) + exp;
}
// Normal range - assumed in format 123.456, integral part in range
String simple = x.toPlainString();
if (simple.length() > max) {
simple = simple.substring(0, max).replaceFirst("\.0*$", "");
}
return simple;
}
这可能会写得更好,以 \.0*
结尾的子字符串,尤其是 toPlainString 等的一些重复用法。 max
太小也是有害的。
是否num
可能以科学/工程符号给出也开放。
我需要确保所有十进制数字的长度始终最多为 15 个字符(包括点),同时尽可能保持精度。因此,它最多必须是 15 个字符,包括“.”。 "E" 用于科学记数法和“-”。
我想的是大数用科学记数法,小数用四舍五入。
例如,对于 1234567891234567.123456789,我会使用科学记数法,但对于 0.123456789123456789,我会四舍五入。
我寻找 java 可以为我执行此操作的库,但我找不到任何可以让我指定表示的字符总数的库。
感谢任何建议或指点。
编辑:更多想法 - 使用 24000E-18 可以毫无损失地表示诸如 0.000000000000024 之类的数字。虽然例如 0.123456789123424 必须遭受一些损失,这当然会使编写任何类型的简单分支算法变得更加困难。
EDIT2:我们用于传输数据的格式是字母数字,并且将数据总共限制为 15 个字符。我必须编写代码来满足格式,以便可以在不触发格式错误的情况下传输数据,同时仍将最大精度保持在限制范围内。
EDIT3:我正在使用它来测试我的功能,但到目前为止,在某些情况下一切都失败了:
Random rnd = new Random();
double n = 100000 + rnd.nextDouble() * 900000;
String res;
double rangeMin = -123456789123456D;
double rangeMax = 123456789123456D;
String val;
for (int i=1;i<=1000;i++) {
n = rangeMin + (rangeMax - rangeMin) * rnd.nextDouble();
val = Double.toString(n);
res = shorteningFunction(val);
System.out.println(val + " " + val.length() + " " + res + " " + res.length());
}
我不想发布不起作用的代码,所以我删除了混乱的代码。
这是伪代码中的思想:
- 将其转换为字符串。
- 检查长度
- 循环:
- 如果长度太长:
- 删除预测。
- 将其转换为字符串。
- 检查长度。
- Return
可以利用BigDecimal
,对于整数部分BigInteger
。
/**
* @param num number representation.
* @param max the maximal length the result should have.
* @return
*/
public static String truncateNumber(String num, int max) {
num = num.replaceFirst("\.0*$", "");
BigDecimal x = new BigDecimal(num);
// Large numbers - integral part
String bigI = x.toBigInteger().toString();
if (bigI.length() > max) {
int expon10 = bigI.length() - max - 1; // - 1 for E
// Digits after E:
if (expon10 == 0) {
++expon10;
} else {
for (int p = expon10; p > 0; ++p) {
++expon10;
p /= 10;
}
}
x = x.movePointLeft(expon10);
String plain = x.toPlainString().substring(0, max - 1 - expon10);
return plain + "E" + expon10;
}
// Tiny numbers - 0.000 (as E-1 already requires 3 positions)
String reprP = x.toPlainString();
if (reprP.startsWith("-0.00")) {
return truncateNumber(num.substring(1), max - 1);
} else if (reprP.startsWith("0.00")) {
String reprE = x.toEngineeringString(); // Does most work.
int epos = reprE.indexOf('E');
String mantissa = reprE.substring(0, epos);
String exp = reprE.substring(epos);
return mantissa.substring(0, Math.min(epos, max - exp.length())) + exp;
}
// Normal range - assumed in format 123.456, integral part in range
String simple = x.toPlainString();
if (simple.length() > max) {
simple = simple.substring(0, max).replaceFirst("\.0*$", "");
}
return simple;
}
这可能会写得更好,以 \.0*
结尾的子字符串,尤其是 toPlainString 等的一些重复用法。 max
太小也是有害的。
是否num
可能以科学/工程符号给出也开放。