Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/132.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 比较uint64_t和float的数值等价性_C++_Floating Point_Cbor - Fatal编程技术网

C++ 比较uint64_t和float的数值等价性

C++ 比较uint64_t和float的数值等价性,c++,floating-point,cbor,C++,Floating Point,Cbor,我正在编写一个协议,它使用二进制表示形式。该标准规定,如果数字值与相应的64位数字相等,则协议可以使用32位浮点数字表示。转换不得导致精度损失 什么32位浮点数可以大于64位整数,并且在数字上与之等效 正在比较float x;uint64_t y;(float)x==(float)y是否足以确保值相等?这种比较会是真的吗 在本规范中,所有数字表示 对于相同的数值,它们是等效的。这意味着 编码器可以将0.0的浮点值编码为整数0。 然而,这也意味着一个应用程序 如果编码器 决定是否需要这些值,

我正在编写一个协议,它使用二进制表示形式。该标准规定,如果数字值与相应的64位数字相等,则协议可以使用32位浮点数字表示。转换不得导致精度损失

  • 什么32位浮点数可以大于64位整数,并且在数字上与之等效
  • 正在比较
    float x;uint64_t y;(float)x==(float)y
    是否足以确保值相等?这种比较会是真的吗

在本规范中,所有数字表示 对于相同的数值,它们是等效的。这意味着 编码器可以将0.0的浮点值编码为整数0。 然而,这也意味着一个应用程序 如果编码器 决定是否需要这些值,例如当浮点值为 比64位整数更紧凑


确实有一些数字是这样的:

2^33可以完美地表示为浮点数,但显然不能表示为32位整数。以下代码应按预期工作:

bool representable_as_float(int64_t value) {
    float repr = value;
    return repr >= -0x1.0p63 && repr < 0x1.0p63 && (int64_t)repr == value;
}
bool可表示为浮点数(int64 t值){
float repr=值;
返回repr>=-0x1.0p63&&repr<0x1.0p63&&(int64_t)repr==value;
}
重要的是要注意,我们基本上是在做(int64_t)(float)值,而不是相反的事情——如果对float的转换失去任何精度,我们会感兴趣

查看repr是否小于int64_t的最大值的检查很重要,因为我们可以调用未定义的行为,否则的话,因为对float的强制转换可能会向上取整到下一个更高的数字(这可能会大于int64_t中可能的最大值)。(感谢@tmyklebu指出这一点)

两个样本:

// powers of 2 can easily be represented
assert(representable_as_float(((int64_t)1) << 33));
// Other numbers not so much:
assert(!representable_as_float(std::numeric_limits<int64_t>::max())); 
//2的幂很容易表示

断言(可表示为浮点数(((int64)t)1)否,需要比较
(长双精度)x==(长双精度)y
在长双精度尾数可以容纳63位的体系结构上。这是因为某些大的长整数在转换为浮点时会失去精度,并与非等效浮点进行比较,但如果转换为长双精度,则不会在该体系结构上失去精度

在x86上使用
gcc-std=c99-mssse3-mfpmath=sse
编译时,以下程序演示了此行为,因为这些设置使用足够宽的长双精度,但防止在计算中隐式使用更高精度的类型:

#include <assert.h>
#include <stdint.h>

const int64_t x = (1ULL<<62) - 1ULL;
const float y = (float)(1ULL<<62);
// The mantissa is not wide enough to store
// 63 bits of precision.

int main(void)
{
  assert ((float)x == (float)y);
  assert ((long double)x != (long double)y);

  return 0;
}
我认为,尽管我可能弄错了,但一个实现可能会在转换过程中以一种失去精度的方式舍入x

另一个可行的策略是比较

extern uint64_t x;
extern float y;
const float z = (float)x;

y == z && (uint64_t)z == x;
这将捕获由于舍入错误导致的精度损失,但如果对z的转换向上舍入,可能会导致未定义的行为。如果在将x转换为z时将转换设置为向零舍入,这将起作用。以下内容基于此。这不需要访问80位
长双精度
或浮点异常,并且应该在任何舍入模式下工作。我相信这应该适用于任何C
float
类型(IEEE754或不是),并且不会导致任何未定义的行为

更新:从技术上讲,这假设是二进制的
浮点
格式,并且
浮点
指数大小足够大,可以表示264:对于标准IEEE754二进制32(您在问题中提到的)来说,这当然是正确的,但对于二进制16来说,则不是

#include <stdio.h>
#include <stdint.h>

int cmp_flt_uint64(float x,uint64_t y) {
  return (x == (float)y) && (x != 0x1p64f) && ((uint64_t)x == y);
}

int main() {
  float x = 0x1p64f;
  uint64_t y = 0xffffffffffffffff;

  if (cmp_flt_uint64(x,y))
    printf("true\n");
  else 
    printf("false\n");
  ;
}
#包括
#包括
int cmp_flt_uint64(浮点x,uint64_t y){
返回(x==(浮点)y)&&(x!=0x1p64f)&&((uint64_t)x==y);
}
int main(){
浮动x=0x1p64f;
uint64_t y=0xffffffffffffff;
如果(cmp_flt_uint64(x,y))
printf(“true\n”);
其他的
printf(“假”);
;
}
这里的逻辑如下:

  • 只有当
    x
    是区间[0264]中的非负整数时,第一个等式才为真
  • 第二个检查
    x
    (因此
    (float)y
    )不是264:如果是这种情况,则
    y
    不能用
    float
    精确表示,因此比较为假
  • x
    的任何剩余值都可以精确转换为
    uint64\t
    ,因此我们进行了转换和比较

什么32位浮点数可以大于64位整数并在数字上与之等效?
无:根据定义,等于
Y
的数字
X
不能大于
Y
。只要32位浮点数是一个整数值,就不会失去将其转换为64位整数的精度。但如果原始浮点不是整数,您将失去精度。当然,该比较可能为真。
1.0f==float(1ull)
这个问题只有在哪些64位整数可以表示为浮点数而不损失精度的情况下才有意义。这也是第一段所说的,第一个要点相当令人困惑。显然,有相当多的数字的这个属性是正确的(大于2^32但小于2^64的2的任意幂)。重要的检查是这样的。如果您获取原始64位整数值,将其转换为浮点,然后将该浮点转换回整数,并获取原始值,那么您可以传输浮点来代替整数;您可以确保另一方可以恢复原始整数(因为您自己刚刚测试过)。你不是通过这样做得到UB吗,比如说,
(int64_t)(float)0x7fffffffffffffll
?在这里,到float的转换是四舍五入的,所以到int64_t的转换将溢出,这就是UB。@tmyklebu公平点。从int->float的转换总是安全的(afaics?),但另一个不是。Whi
#include <stdio.h>
#include <stdint.h>

int cmp_flt_uint64(float x,uint64_t y) {
  return (x == (float)y) && (x != 0x1p64f) && ((uint64_t)x == y);
}

int main() {
  float x = 0x1p64f;
  uint64_t y = 0xffffffffffffffff;

  if (cmp_flt_uint64(x,y))
    printf("true\n");
  else 
    printf("false\n");
  ;
}