C语言 如果将0和1之间的32位浮点数转换为int/uint,它们是否可以进行比较(具有相同的结果)?

emeijp43  于 2023-11-17  发布在  其他
关注(0)|答案(7)|浏览(134)

当将两个float32表示为int32或uint32(使用union或casting指针类型)时,在有限的范围内(0..1),这些值是否可以相互比较,这将匹配作为浮点数执行时的比较结果?
范例:

int float_as_int(float f) {
    union { int i; float f; } u;
    u.f = f;
    return u.i;
}

/* Assert the following is always true for values in a limited range. */
void assert_compare_works_as_expected(float a, float b) {
    assert(float_as_int(a) < float_as_int(b) == a < b);
}

/* Psudocode */
int main() {
    for f in 0..1 {
        assert_compare_works_as_expected(f_prev, f);
        f_prev = f;
    }
}

字符串
如果不是,什么浮点数范围直接Map到int,以便可以进行比较?

s2j5cfk0

s2j5cfk01#

详细的答案是:是的,但有多个限制和警告。将01范围内的浮点数作为整数进行比较在许多情况下可能会产生正确的结果。事实上,值不需要限制在此范围内:

  • 假设float类型具有32位单精度IEEE-754表示,而int类型具有32位2的补码表示,没有填充位(即intint32_t);
  • 假设float的值都不是NaN;
  • 假设数值为正常或次正常形式;
  • 假设floatint表示在内存中具有相同的字节序;
  • 如果两个值相等,则作为int进行比较将产生正确的结果,除非您正在比较+0.0-0.0
  • 如果两个值不都是负数,则作为int的比较可能确实会产生正确的结果,但C标准并不保证,并且在大多数情况下,将float的值作为int访问违反了严格的别名规则。
  • 如果两个值都为负,并且其他约束条件匹配,则比较结果应取反。

关于你的问题:某些整数值实际上可以具有与intfloat相同的表示,特别是0,但浮点值-0.0具有不同的表示,但与float相比等于0.0。只有两种其他特殊情况的整数具有与int32_t和IEEE相同的表示。754 float13189269650x4E9D3A75)和-8342148020xCE46E46E)。
您可以在这里查看IEEE格式的浮点值的实际表示,这是非常常见的,但不是C标准所要求的:

对于您的特定情况,如果您知道值在01的范围内,您应该处理的唯一特殊情况是负0。您可以通过屏蔽值将符号设置为正数,代码如下所示:

/* return 0 if p1 and p2 point to the same float value,
   -1 if p1 points to a smaller value and
   +1 otherwise.
 */
int compare_floats_as_ints(const int32_t *p1, const int32_t *p2) {
    int32_t i1 = *p1 & 0x7fffffff;
    int32_t i2 = *p2 & 0x7fffffff;
    return (i1 > i2) - (i1 < i2);
}

字符串

g6ll5ycj

g6ll5ycj2#

我解释你的问题的方式是:当将内存中浮点数的位模式解释为int时,通常的比较运算符对浮点数和整数的行为是否相同。
答案是否定的。严格地说,C标准有几种可能的整数表示法和更多可能的浮点数表示法。但即使限制我们自己到几乎每个人都做的-二进制补码整数和ieee 754 binary 32浮点数,答案仍然是否定的。
最简单的例子:浮点数有两个零。位模式0x 00000000和位模式0x 80000000表示数字0和-0。它们在浮点数时都比较相等,如果被解释为整数,它们就不会相等。然后你有两个无穷大,还有很多位模式表示NaN。
你可以列举特殊情况并避免它们,然后我很确定整数比较会起作用,但是无论你从整数比较中获得什么性能增益(我假设这是这里的重点),你都会在避免特殊情况时失去更多。
回应您的最后一次编辑:是的,在0到1的范围内,ieee 754浮点数的位模式是这样的,当被解释为整数时,普通的整数比较运算符将按照你想要的方式工作。这是一个坏主意,但它会工作。

y3bcpkx1

y3bcpkx13#

如果将0和1之间的32位浮点数转换为int/uint,它们是否可以进行比较(具有相同的结果)?
使用union可以比较float的范围(0和1)和如果下面的所有列表都可以被保险。
否则,一般的答案是否定的。它可能有效,也可能无效。

union x {
  float f;
  int32_t i;
  uint32_t u;
} a,b ;
a.f = foo(); 
b.f = foo(); 
// for all a.f and b.f in range
assert((a.u > b.u) == (a.f > b.f));
assert((a.u < b.u) == (a.f < b.f));
assert((a.u == b.u) == (a.f == b.f));
// also for a.i

字符串

  1. floatbinary32
    1.整数类型没有填充。(例如:(u)intN_t)类型。
    1.整数可以是任何编码:2的补码,1的补码,符号幅度
    1.这个整数和float有相同的字节序。C不需要这个。
    1.整数/FP类型的大小是相同的。(这是在问题中暗示的)。
    这也适用于[+0.0...+INF]的扩展范围。它不适用于-0.0。
    为了比较2 any 有限/无限float,给定相同的endian-ness,预期的float编码和上述条件:
int32_t sequence_f(float x) {
  union {
    float f;
    int32_t i32;
    uint32_t u32;
  } u;
  assert(sizeof(float) == sizeof(uint32_t));
  u.f = x;
  if (u.i32 < 0) {
    u.u32 = 0x80000000 - u.u32;
  }
  //  printf("% 16.8e % 11d\n", x, u.i32);
  return u.i32;
}

// return + +,0,- without FP math.
// valid for all finite/infinite values, not valid for NaN
// +0.0 -0.0 compare the same.
int compare(float fa, float fb) {
  int32_t ia = sequence_f(fa);
  int32_t ib = sequence_f(fb);
  return (ia > ib) - (ia < ib);
}


这对于需要执行FP比较的嵌入式应用程序很有用,但对于其他FP运算则不适用。
通常,由于抗锯齿,造型是UB。

hec6srdp

hec6srdp4#

答案第一

是的,您可以比较float的二进制表示,只要它们使用IEEE-754格式,整数是2的补码,您正在比较正值或正值与负值,并且正确处理特殊情况。
Snippet on Ideone.com in C

解释

IEEE-754浮点数(4字节)具有以下结构:

s::::::::.......................    meaning
    31                              0    bit #

    s = sign bit, : = exponent, . = mantissa

字符串
该值被编码为mantissa * 2 ^ exponent。指数范围为0x000xFF,它表示值-1271280x7F对应于0)。
尾数表示 * 一个二进制分数 *,小数的左边总是隐含1b。尾数的每一位表示2 ^ -(n+1)的值,其中n是从左边开始的一位的索引。尾数的最左边的位具有0.5的值,左边第二位0.25,左边第三位0.125,等
0.5将具有尾数b0000...b1.0)和指数0x7E-1)。
0.03将具有尾数b1110...b1.1110...)和指数0x79-6)。
这些值将存储为:

0 01111110 00000000000000000000000    (0x3F000000)
0 01111001 11101011100001010001111    (0x3CF5C28F)


可以存储的最小值是1.0 * 2 ^ -127(尾数和指数的所有位都为零)。这是一个特殊情况,代表0。指数值0xFF是为NaNinfinity保留的。
Floating point number representation
如果使用signed int,则可以将负的float值与正的signed int值进行比较,因为符号标志存储在signed intfloat的最高位中。您不能像这样比较两个负的float值,因为int的否定不是仅通过反转符号标志来完成的。

lymnna71

lymnna715#

答案是肯定的,但条件是:

  • float和int的大小相同。
  • 输入是正数,浮点值,不包括:

-0.0-infnan-nan
详细信息:

  • signed intunsigned int都可以用于匹配比较。
  • 假设IEEE 754 32位浮点。
  • int和float都需要是相同的endian (是否存在不是这种情况的架构?)
  • 不只是0-10.0..FLT_MAXinf可以比较。
  • 为了完整性,负值:-0.0..-FLT_MAX也可以比较,但总是有一个翻转的顺序,例如:

assert(float_as_int(a) > float_as_int(b) == a < b);
在这种情况下,可以比较表示为int的2x浮点数,得到相同的结果。
下面是一个证据,证明这是可行的:
C代码,测试完整的无符号浮点范围。

#include <math.h>
#include <float.h>
#include <stdio.h>

int main() {
    unsigned step = 0;
    union {
        float f;
        unsigned u;
    } value;

    value.f = 0.0;

    unsigned u_prev = value.u;

    while (value.f != FLT_MAX) {
        value.f = nextafterf(value.f, FLT_MAX);
        unsigned u = value.u;
        if (u <= u_prev) {
            printf("At value %f, step %u, comparisons don't match\n", value.f, step);
            break;
        }
        u_prev = u;
        step++;
    }
    printf("Tested %u times\n", step);

    return 0;
}

字符串
一个Python 3脚本,用nextafterf检查0-1之间的所有值,注意这很慢 (因此上面的C版本)

def main():
    from struct import pack, unpack
    as_u32_prev = None
    value = 0.0
    while True:
        # transmute float to int
        as_u32 = unpack('I', pack('f', value))
        if as_u32_prev is not None:
            if as_u32_prev > as_u32:
                raise Exception("f32/u32 comparisons don't match")
        as_u32_prev = as_u32
        if value == 1.0:
            break
        value = nextafterf(value, 1.0)

# Boiler Plate, see: https://stackoverflow.com/questions/6063755

import ctypes
import sys
from sys import platform as _platform

if _platform == "linux" or _platform == "linux2":
    _libm = ctypes.cdll.LoadLibrary('libm.so.6')
    _funcname_f = 'nextafterf'
elif _platform == "darwin":
    _libm = ctypes.cdll.LoadLibrary('libSystem.dylib')
    _funcname_f = 'nextafterf'
elif _platform == "win32":
    _libm = ctypes.cdll.LoadLibrary('msvcrt.dll')
    _funcname_f = '_nextafterf'
else:
    # these are the ones I have access to...
    # fill in library and function name for your system math dll
    print("Platform", repr(_platform), "is not supported")
    sys.exit(0)

nextafterf = getattr(_libm, _funcname_f)
nextafterf.restype = ctypes.c_float
nextafterf.argtypes = [ctypes.c_float, ctypes.c_float]

main()


请注意,这里的其他答案说,这不会在所有情况下工作,我很想知道在这种情况下,在这个答案的例子会失败,(除了架构,其中float和int不是4字节的大小)

omqzjyyz

omqzjyyz6#

根据我的理解,你想从内存中读取浮点数作为无符号/有符号整数,我会给你给予答案。
不能将floatsignedunsigned int(32位)进行比较。
例如,int中的数字4被表示为0x00000004,而相同的数字4以浮点数(根据IEEE 754标准)表示为0x40800000

siv3szwd

siv3szwd7#

无论你是将union中的uint与float进行比较,还是将float本身进行比较,都是一样的。
如果你有两个操作:

union {
  float fl;
  uint32_t ui;
} a, b

float a.fl = (math expressions);
float b.fl = (another math expressions); // both giving theoretically the same results

// comparition of

if(a.fl == b.fl) .... //UB - as float shall not be compared for being equal
if(a.ui == b.ui) .... //UB - as ui is an unsigned representation of the bytes of fl

字符串

相关问题