将浮点值转换为循环范围?

问题描述 投票:0回答:1

我不确定我是否使用了正确的术语,但有时我发现自己需要以循环方式将浮点值规范化为一个范围。 (例如,对于表示旋转的值、整个象限的特殊情况旋转或消除整圈的冗余旋转,这可能很有用。)

例如,假设循环范围为1.0,则结果应为:0 <= result < 1.0.
输入 0.25、1.25、2.25 等应全部变为 0.25。

当然,这就是模运算符

%
supposited 的作用:

System.out.println(1.25 % 1); // 0.25

但是对于负输入它仍然会产生负结果:

System.out.println(-1.75 % 1); // -0.75, rather than 0.25 as wanted

因此,几年了,每当我需要此功能时,我就一直在编写此模式:

x %= range;
if (x < 0) x += range;

今天我发现了一个错误:从非常小的负输入(例如,

x = -1e-20
)开始,
x += range
的浮点结果四舍五入为exactly
range
,因此超出范围。

这导致了:

static double cyclic(double x, double range) {
    x %= range;
    if (x < 0) {
        x += range;
        if (x >= range) x = 0;
    }
    return x;
}

尽管每一行都有其存在的理由,但整体却很怪诞。 (它有两种口味,一种是

float
,另一种是
double
,是双倍的怪诞。)

我想知道:

  • 我上面的“循环”函数最终对所有浮点异常都具有鲁棒性吗?

  • 有更好的解决方案吗?这感觉不应该这么复杂。我觉得我错过了一个技巧。

java floating-point modulo ieee-754
1个回答
0
投票

看起来你以前是用Python写的)

有了Java中double类型变量的划分,一切就没那么简单了。

System.out.println(1.25 % 1); // 0.25
But for ==>  System.out.println(1.1 % 1); // 0.10000000000000009

不用if/if就可以得到模除后的变量值 例如,像这样(实际上是两行)

List<Double> ld = List.of(1.25, 2.25, 3.25, -4.25, -5.25);
        for (double var : ld) {
            DecimalFormat decimalFormat = new DecimalFormat( "#.#" );
            System.out.println(Double.valueOf(decimalFormat.format(Math.abs(var) % 1)));
        }

或者你写详细一点

List<Double> ld = List.of(1.25, 2.25, 3.25, -4.25, -5.25);
            for (double var : ld) {
              double varWithoutSign = Math.abs(var);
              double remainderOfDivision = varWithoutSign % 1;
              DecimalFormat decimalFormat = new DecimalFormat( "#.#" );
              String resultStr = decimalFormat.format(remainderOfDivision);
              double result = Double.valueOf(resultStr);
              System.out.println(result);
              }
© www.soinside.com 2019 - 2024. All rights reserved.