Java:如何通过Float.floatToIntBits进行增量操作?

3

我需要原子地增加一个浮点数的值。我通过调用Float.floatToIntBits获取其int值。如果我只是做一个i++,然后将其转换回float,它不会给我预期的值。那么我该怎么做呢?

(我正在尝试通过AtomicInteger创建AtomicFloat,因此有这个问题)。

编辑:这是我所做的:

Float f = 1.25f;
int i = Float.floatToIntBits(f);
i++;
f = Float.intBitsToFloat(i);

我希望得到2.25,但实际上得到了1.2500001。


1
预期结果是什么?实际结果是什么?可以提供更多的描述吗? - Chandra Sekhar
如果您能提供一个期望结果和实际结果的例子,那将会很有帮助。问题很可能出在您的期望上... - Jon Skeet
2
“增加浮点数”是什么意思?是将其加1,还是获取下一个更大的浮点值(加1 ULP)? - Christian Semrau
@Christian Semrau,编辑了问题以提供我想要的示例。 - shrini1000
1
请注意,对于大的浮点数值(几百万及以上),由于四舍五入,f+1 的值等于 f,因此加一并不总是增加一个值。 - Christian Semrau
显示剩余2条评论
2个回答

5

原因是从floatToIntBits得到的位表示:

  1. 符号
  2. 指数
  3. 尾数

排列如下:

Repr:  Sign   Exponent          Mantissa
Bit:    31   30......23  22.....................0

将存储这些字段的整数加1不会使其表示的浮点数值增加1。

我正在尝试通过AtomicInteger创建AtomicFloat,因此有这个问题

我在回答这个问题时确切地做到了这一点:

要添加将浮点数增加1的功能,您可以复制{{link2:AtomicIntegerincrementAndGet的代码}}(并从int更改为float):

public final float incrementAndGet() {
    for (;;) {
        float current = get();
        float next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}

(请注意,如果您想将浮点数增加到最小可能值,则可以使用上述代码,并将current + 1更改为current +Math.ulp(current)。)

谢谢答复。但是你展示的incrementAndGet代码不会使用CAS操作,对吗(AtomicInteger的方式)?所以它必须按照通常的方式进行同步? - shrini1000
我在我的答案中发布的incramentAndGet方法旨在包含在我在另一个答案中描述的AtomicFloat类中。 - aioobe
顺便说一下,我看了一下AtomicInteger以及它如何使用'unsafe'进行CAS。'unsafe'没有任何API可以比较和交换float/double。这就是为什么我们没有直接支持AtomicFloat/AtomicDouble的原因吗? - shrini1000
不这么想。我在我链接的答案中发布了“官方”原因。 - aioobe

2

原文:

原子部分可以像aioobe链接中展示的那样在包装类上实现compareAndSetAtomicInteger的增量运算符就是这样实现的。

增量部分是一个完全不同的问题。根据您对“增加浮点数”的理解,它要么需要您将数字加一,要么需要您将其增加ULP。对于后者,在Java 6中,Math.nextUp方法就是您要找的方法。对于减少一个ULP,Math.nextAfter方法很有用。

其中,link1、link2和link3均为超链接,此处无法翻译出具体含义。

1
请注意,nextAfter 需要一个额外的“虚拟”参数,例如 Float.MAX_VALUE,而 Math.ulp 直接给出到下一个浮点数的距离。 - aioobe
正如我所看到的,Math.nextUp(f)(Java 6)返回f+ulp(f)的值,而不需要nextAfter(f, INFINITY)所需的额外参数,并且可能比计算f+ulp(f)更快。 - Christian Semrau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接