gpt4 book ai didi

java - 两个值之间唯一 `double` 的数量

转载 作者:塔克拉玛干 更新时间:2023-11-02 19:56:53 24 4
gpt4 key购买 nike

我在下面提供了这个问题的答案(我没有找到“回答自己的问题”按钮)

原始问题

在 Java 中,我们有浮点类型 double,它在内存中被编码为 64 位。由此我们知道它最多可以取 2^64 可能的值,减去一些特殊值。我们可以一一列举。

例程 Math.nextUp(d)Math.nextDown(d),给定一个 doubled,将分别计算下一个更大和更小的 double。现在我想知道如何计算从一个 double abdouble 步数,即我的方法 difference(a,b) 应该按如下方式工作:

assume a fixed, given a.

b = .... | difference(a, b)
===========================================================
a | 0
Math.nextUp(a) | 1
Math.nextDown(a) | 1
Math.nextUp(Math.nextUp(a)) | 2
Math.nextDown(Math.nextDown(a)) | 2

...等等。

在Java OpenJDK中,这两个方法的实现如下:

public static double nextUp(double d) {
if( Double.isNaN(d) || d == Double.POSITIVE_INFINITY)
return d;
else {
d += 0.0d;
return Double.longBitsToDouble(Double.doubleToRawLongBits(d) +
((d >= 0.0d)?+1L:-1L));
}
}

public static double nextDown(double d) {
if (Double.isNaN(d) || d == Double.NEGATIVE_INFINITY)
return d;
else {
if (d == 0.0)
return -Double.MIN_VALUE;
else
return Double.longBitsToDouble(Double.doubleToRawLongBits(d) +
((d > 0.0d)?-1L:+1L));
}
}

我可以安全地做类似的事情吗,或者这只是因为他们只考虑 1 的递增和递减才有效,也就是说,我可能会遇到指数问题吗?我强烈假设后一种情况,想知道实现我的目标的正确方法是什么?

同样:我希望它适用于任意 double,即 difference(1e-3,3.442e201) 应该返回数字 Math.nextUp 我需要从 1e-33.44e201 的步骤。显然,在这种情况下,仅迭代和计数 Math.nextUp 是行不通的。

非常感谢, 托马斯。

问题的答案

感谢@Thilo 的评论,事实证明计算差异实际上很容易。好吧,至少看起来是那么简单。

Java代码如下:

/** Some mathematical utilities */
public final class MathUtils {

/**
* The number of unique {@code double} values between {@code a} and
* {@code b}.
*
* @param a
* the first {@code double}
* @param b
* the second {@code double}
* @return the steps between them, or {@code -1} if either value is
* {@link Double#NaN} or both are infinities of different signs
*/
public static final long difference(final double a, final double b) {
final long bitsA;
double useA, useB, temp;

if ((a != a) || (b != b)) { // take are of NaN
return -1L;
}
useA = (a + 0d);
useB = (b + 0d);
if (useA > useB) {
temp = useB;
useB = useA;
useA = temp;
}
if (useA == useB) {
return 0L;
}
if (useA <= Double.NEGATIVE_INFINITY) {
return -1L;
}
if (useB >= Double.POSITIVE_INFINITY) {
return -1L;
}

if (useA < 0d) {
bitsA = Double.doubleToRawLongBits(-useA);
if (useB < 0d) {
return (bitsA - Double.doubleToRawLongBits(-useB));
}
return (bitsA + Double.doubleToRawLongBits(useB));
}
return (Double.doubleToRawLongBits(useB)
- Double.doubleToRawLongBits(useA));
}
}

这里有一些基本的 JUnit 测试来确认结果是否符合预期:

import java.util.Random;

import org.junit.Assert;
import org.junit.Test;


/**
* A test for math utils
*/
public class MathUtilsTest {

/** the constructor */
public MathUtilsTest() {
super();
}

/** test step difference between two values */
@Test(timeout = 3600000)
public void testDifferenceBetweenTwoValues() {
final Random random;
double start, end;
int starts, iteration;

random = new Random();

for (starts = 333; (--starts) >= 0;) {
end = start = -(1d / Math.log(1d - random.nextDouble()));
for (iteration = 0; iteration < 3333; iteration++) {
Assert.assertEquals(iteration, MathUtils.difference(start, end));
Assert.assertEquals(iteration, MathUtils.difference(end, start));
end = Math.nextUp(end);
}
}
}

/**
* test the "step" difference of two values, one of which is negative,
* the other one being positive
*/
@Test(timeout = 3600000)
public void testDifferenceBetweenTwoValuesOfDifferentSign() {
double start, end;
int iteration;

end = start = 0d;
for (iteration = 0; iteration < 333333; iteration++) {
Assert.assertEquals(
(MathUtils.difference(start, 0d) + //
MathUtils.difference(0d, end)),
MathUtils.difference(start, end));
Assert.assertEquals(
(MathUtils.difference(start, 0d) + //
MathUtils.difference(0d, end)),
MathUtils.difference(end, start));
start = Math.nextAfter(start, Double.NEGATIVE_INFINITY);
end = Math.nextUp(end);
}
}

/** test the border cases of the step difference */
@Test(timeout = 3600000)
public void testDifferenceBetweenTwoValuesBorderCases() {
Assert.assertEquals(0L, MathUtils.difference(0d, 0d));
Assert.assertEquals(0L, MathUtils.difference(0d, -0d));
Assert.assertEquals(0L, MathUtils.difference(-0d, 0d));
Assert.assertEquals(0L, MathUtils.difference(-0d, -0d));

Assert.assertEquals(1L, MathUtils.difference(0d, Double.MIN_VALUE));
Assert.assertEquals(1L, MathUtils.difference(Double.MIN_VALUE, 0d));
Assert.assertEquals(1L, MathUtils.difference(-0d, Double.MIN_VALUE));
Assert.assertEquals(1L, MathUtils.difference(Double.MIN_VALUE, -0d));

Assert.assertEquals(1L, MathUtils.difference(0d, -Double.MIN_VALUE));
Assert.assertEquals(1L, MathUtils.difference(-Double.MIN_VALUE, 0d));
Assert.assertEquals(1L, MathUtils.difference(-0d, -Double.MIN_VALUE));
Assert.assertEquals(1L, MathUtils.difference(-Double.MIN_VALUE, -0d));

Assert.assertEquals(2L,
MathUtils.difference(Double.MIN_VALUE, -Double.MIN_VALUE));
Assert.assertEquals(2L,
MathUtils.difference(-Double.MIN_VALUE, Double.MIN_VALUE));

Assert.assertEquals((1L << 52L),
MathUtils.difference(0d, Double.MIN_NORMAL));
Assert.assertEquals((1L << 52L),
MathUtils.difference(Double.MIN_NORMAL, 0d));
Assert.assertEquals((1L << 52L),
MathUtils.difference(-0d, Double.MIN_NORMAL));
Assert.assertEquals((1L << 52L),
MathUtils.difference(Double.MIN_NORMAL, -0d));

Assert.assertEquals((1L << 52L),
MathUtils.difference(0d, -Double.MIN_NORMAL));
Assert.assertEquals((1L << 52L),
MathUtils.difference(-Double.MIN_NORMAL, 0d));
Assert.assertEquals((1L << 52L),
MathUtils.difference(-0d, -Double.MIN_NORMAL));
Assert.assertEquals((1L << 52L),
MathUtils.difference(-Double.MIN_NORMAL, -0d));

Assert.assertEquals((2L << 52L),
MathUtils.difference(Double.MIN_NORMAL, -Double.MIN_NORMAL));
Assert.assertEquals((2L << 52L),
MathUtils.difference(-Double.MIN_NORMAL, Double.MIN_NORMAL));

Assert.assertEquals(0L, MathUtils.difference(Double.POSITIVE_INFINITY,
Double.POSITIVE_INFINITY));
Assert.assertEquals(0L, MathUtils.difference(Double.NEGATIVE_INFINITY,
Double.NEGATIVE_INFINITY));
Assert.assertEquals(-1L, MathUtils.difference(Double.POSITIVE_INFINITY,
Double.NEGATIVE_INFINITY));
Assert.assertEquals(-1L, MathUtils.difference(Double.NEGATIVE_INFINITY,
Double.POSITIVE_INFINITY));

Assert.assertEquals(-1L, MathUtils.difference(Double.NaN, Double.NaN));
Assert.assertEquals(-1L,
MathUtils.difference(Double.POSITIVE_INFINITY, Double.NaN));
Assert.assertEquals(-1L,
MathUtils.difference(Double.NEGATIVE_INFINITY, Double.NaN));
Assert.assertEquals(-1L,
MathUtils.difference(Double.NaN, Double.POSITIVE_INFINITY));
Assert.assertEquals(-1L,
MathUtils.difference(Double.NaN, Double.NEGATIVE_INFINITY));

Assert.assertEquals(-1L,
MathUtils.difference(0d, Double.NEGATIVE_INFINITY));
Assert.assertEquals(-1L,
MathUtils.difference(0d, Double.POSITIVE_INFINITY));
Assert.assertEquals(-1L, MathUtils.difference(0d, Double.NaN));

Assert.assertEquals(-1L,
MathUtils.difference(Double.POSITIVE_INFINITY, 0d));
Assert.assertEquals(-1L,
MathUtils.difference(Double.NEGATIVE_INFINITY, 0d));
Assert.assertEquals(-1L, MathUtils.difference(Double.NaN, 0d));

Assert.assertEquals(-1L,
MathUtils.difference(1d, Double.NEGATIVE_INFINITY));
Assert.assertEquals(-1L,
MathUtils.difference(1d, Double.POSITIVE_INFINITY));
Assert.assertEquals(-1L, MathUtils.difference(1d, Double.NaN));

Assert.assertEquals(-1L,
MathUtils.difference(Double.POSITIVE_INFINITY, 1d));
Assert.assertEquals(-1L,
MathUtils.difference(Double.NEGATIVE_INFINITY, 1d));
Assert.assertEquals(-1L, MathUtils.difference(Double.NaN, 1d));

Assert.assertEquals(-1L,
MathUtils.difference(-1d, Double.NEGATIVE_INFINITY));
Assert.assertEquals(-1L,
MathUtils.difference(-1d, Double.POSITIVE_INFINITY));
Assert.assertEquals(-1L, MathUtils.difference(-1d, Double.NaN));

Assert.assertEquals(-1L,
MathUtils.difference(Double.POSITIVE_INFINITY, -1d));
Assert.assertEquals(-1L,
MathUtils.difference(Double.NEGATIVE_INFINITY, -1d));
Assert.assertEquals(-1L, MathUtils.difference(Double.NaN, -1d));
}
}

对于代码尚未涵盖的任何反指示或问题,我将不胜感激。

干杯, 托马斯。

最佳答案

看来您可以只取两个值的 doubleToRawLongBits 并减去它们(加上一些额外的逻辑来处理符号、过零和 Inf/NaN)。

由于nextUp所做的只是加一,所以减法的结果应该与步数一致。

关于java - 两个值之间唯一 `double` 的数量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33557419/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com