保持整数的精度,以双精度形式传递

Keeping precision of integers, passed as doubles

提问人:MiamiBeach 提问时间:3/16/2023 最后编辑:LWChrisMiamiBeach 更新时间:3/22/2023 访问量:117

问:

我有一项服务,从外部世界接收双倍。我确信所有双打在小数点后只有 4 位数字。需要强调的是,向我发送数据的原始系统使用 5 位整数,因此这 5 位数字“后面”的所有数据都是毫无意义且具有误导性的。为了保持精确度,我想将传入的双精度转换为整数,使用整数,然后将它们转换回双精度以返回。我看到从理论上讲,知道输入总是在小数点后有 4 位数字,考虑到我只对这些数字应用加法/减法,我根本不应该在这里失去精度。不涉及除法和乘法。

  • 收到像 1.2345 这样的双倍(我知道无法收到 1.23456789)
  • 我将其乘以 10,000 并转换为整数
  • 现在我可以进行精确的整数计算(仅加法和减法),这意味着结果必须精确(与使用双精度的加法和减法相反)
  • 在返回之前,我再次将结果除以 10,000 以转换回双倍

不幸的是,我已经失去了转换的准确性。我的以下测试代码显示了这一点:

double[] examples = {1.3456, 6.0000, 1.9999, 7.0001};
int[] inInt = new int[4];
double[] outDouble = new double[4];

// Input conversion from double to int
for (int i = 0; i < 4; i++) {
    inInt[i] = (int) (examples[i] * 10_000);
}

// Integer calculations elided

// Output conversion back to double
for (int i = 0; i < 4; i++) {
    outDouble[i] = ((double) inInt[i] / 10_000.0);
}

// Show
System.out.println(Arrays.toString(examples));
System.out.println(Arrays.toString(inInt));
System.out.println(Arrays.toString(outDouble));

上面的代码给了我

[1.3456, 6.0, 1.9999, 7.0001]
[13455, 60000, 19999, 70001]
[1.3455, 6.0, 1.9999, 7.0001]

因此,您可以看到,虽然第 2 个到第 4 个示例有效,但转换为 int 时丢失了精度,因为它变成了 而不是 .1.34561345513456

是否有可能完全消除这里的精度损失?

我只看到一个选项:double >字符串>整数>运算,使用整数>构造字符串>构建 double,但这非常费力。我正在使用 Java,但我认为这在这里并不重要。

PS 这里
所说的“精度损失”是指由于传入数据的双重性质而导致的任何精度损失,即对我来说,“没有精度损失”的计算是对整数进行的计算,整数是从传入数据构建的。

Java 算法 精度

评论

0赞 Bohemian 3/16/2023
请准确描述您所说的“精度损失”是什么意思,您期望什么以及您得到什么“不好”。
3赞 Steve Summit 3/16/2023
你说“我有一项服务,从外部世界接收双倍”。它是以字符串形式接收它们,还是以双精度形式接收它们?如果它以字符串形式接收它们,那么您希望(以某种方式)将它们转换为缩放整数,而无需转换为 first。但是,如果它接收它们为 ,那么你肯定需要在乘法后四舍五入,RaffleBuffle 的答案适用。doubledouble
3赞 Steve Summit 3/16/2023
但最根本的问题是,如果你真的把它们当作,你说的“1.2345来找我,我知道1.23456789不能来”是没有意义的。你永远不会得到 1.2345;相反,你实际上会得到 1.234499999999(之后还有大约 40 个非零数字)。double
2赞 Steve Summit 3/16/2023
不涉及除法和乘法...... 除了乘以 10000。不是开玩笑:那是你的问题。
2赞 Basil Bourque 3/16/2023
如果您想要准确性而不是速度,请使用而不是 .您的服务应返回您解析为 .BigDecimaldoubleBigDecimal

答:

0赞 Eritrean 3/16/2023 #1

使用引用而不是基元进行输入,并转换为 为了准确起见。DoubledoubleBigDecimal

public static void main(String args[]) {

    Double[] n  = {1.3456, 6.0000, 1.9999, 7.0001};
    int[] in    = new int[4];
    double[] od = new double[4];

    final BigDecimal tenK = BigDecimal.valueOf(10000);

    for (int i=0;i<4;i++) {
        in[i] = BigDecimal.valueOf(n[i]).multiply(tenK).intValue();
    }

    for (int i=0;i<4;i++) {
        od[i] = in[i] / tenK.doubleValue();
    }

    System.out.println(Arrays.toString(n)); 
    System.out.println(Arrays.toString(in));
    System.out.println(Arrays.toString(od));
}

如果转换是重复任务,则可以将其提取到函数中:

public static void main(String args[]) {

    Double[] n  = {1.3456, 6.0000, 1.9999, 7.0001};
    int[] in    = new int[4];
    double[] od = new double[4];

    final BigDecimal tenK = BigDecimal.valueOf(10000);

    final DoubleToIntFunction multiplyBYTenK = d -> BigDecimal.valueOf(d).multiply(tenK).intValue();
    final IntToDoubleFunction divideByTenK   = i -> i / tenK.doubleValue();

    Arrays.setAll(in, i -> multiplyBYTenK.applyAsInt(n[i]));
    Arrays.setAll(od, i -> divideByTenK.applyAsDouble(in[i]));

    System.out.println(Arrays.toString(n)); //
    System.out.println(Arrays.toString(in));
    System.out.println(Arrays.toString(od));
}

评论

1赞 Eric Postpischil 3/16/2023
没有解释的代码不是一个好的答案。代码的工作方式很重要。详细地说,代码的作用很重要。概念很重要。为什么以前的代码不起作用很重要。为什么新代码有效很重要。