我不确定我是否使用了正确的术语,但有时我发现自己需要以循环方式将浮点值规范化为一个范围。 (例如,对于表示旋转的值、整个象限的特殊情况旋转或消除整圈的冗余旋转,这可能很有用。)
例如,假设循环范围为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
的浮点结果四舍五入为exactlyrange
,因此超出范围。
这导致了:
static double cyclic(double x, double range) {
x %= range;
if (x < 0) {
x += range;
if (x >= range) x = 0;
}
return x;
}
尽管每一行都有其存在的理由,但整体却很怪诞。 (它有两种口味,一种是
float
,另一种是double
,是双倍的怪诞。)
我想知道:
我上面的“循环”函数最终对所有浮点异常都具有鲁棒性吗?
有更好的解决方案吗?这感觉不应该这么复杂。我觉得我错过了一个技巧。
看起来你以前是用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);
}