C++ 比较uint64_t和float的数值等价性
我正在编写一个协议,它使用二进制表示形式。该标准规定,如果数字值与相应的64位数字相等,则协议可以使用32位浮点数字表示。转换不得导致精度损失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。 然而,这也意味着一个应用程序 如果编码器 决定是否需要这些值,
- 什么32位浮点数可以大于64位整数,并且在数字上与之等效
- 正在比较
是否足以确保值相等?这种比较会是真的吗float x;uint64_t y;(float)x==(float)y
确实有一些数字是这样的: 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位长双精度或浮点异常,并且应该在任何舍入模式下工作。我相信这应该适用于任何Cfloat
类型(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");
;
}