Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/61.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++ 将32位无符号整数精确转换为范围(-1;1)中的浮点_C++_C_Algorithm_Cuda_Floating Point - Fatal编程技术网

C++ 将32位无符号整数精确转换为范围(-1;1)中的浮点

C++ 将32位无符号整数精确转换为范围(-1;1)中的浮点,c++,c,algorithm,cuda,floating-point,C++,C,Algorithm,Cuda,Floating Point,根据,一半的浮点数在区间[-1,1]内。您能否建议如何利用这一事实来取代将32位无符号整数转换为浮点数的天真转换(同时保持均匀分布) 朴素的代码: uint32_t i = /* randomly generated */; float f = (float)i / (1ui32<<31) - 1.0f; uint32_t i=/*随机生成*; float f=(float)i/(1ui32您可以使用double进行计算,这样您就不会丢失uint32\u t值的任何精度,然后将结果

根据,一半的浮点数在区间[-1,1]内。您能否建议如何利用这一事实来取代将32位无符号整数转换为浮点数的天真转换(同时保持均匀分布)

朴素的代码:

uint32_t i = /* randomly generated */;
float f = (float)i / (1ui32<<31) - 1.0f;
uint32_t i=/*随机生成*;

float f=(float)i/(1ui32您可以使用
double
进行计算,这样您就不会丢失
uint32\u t
值的任何精度,然后将结果分配给
float

float f = (double)i / (1ui32<<31) - 1.0;
float f=(double)i/(1ui32我建议(如果您希望避免除法并使用1.0*2^-32的可精确表示的浮点起始值):


如果您放弃均匀分布约束,则仅在32位整数算术上可行:

//---------------------------------------------------------------------------
浮点i32到f32(整数x)
{
国际贸易;
并集_f32//半结果
{
浮点f;//32位浮点
DWORD u;//32位uint
}y;
//边缘案例
如果(x==0x00000000)返回0.0f;
如果(x<-0x1FFFFFF)返回-1.0f;
如果(x>+0x1ffffff)返回+1.0f;
//转化
y、 u=0;//重置位
if(x>23)&63)-64;//高位6位->指数-1,…-64(非7位以避免非规范化数字)
y、 u |=(exp+127)23)&255;exp-=127;//指数偏差和位位置
如果(exp
有没有办法在不使用双精度浮点的情况下实现这一点

在不对
float
的内部进行过多假设的情况下:

移位
u
直到设置了最高有效位,将
浮点值减半

“保持均匀分布”

50%的
uint32\u t
值将在[0.5…1.0]中
25%的
uint32\u t
值将在[0.25…0.5]中
12.5%的
uint32\u t
值将在[0.125…0.25]中
6.25%的
uint32\u t
值将位于[0.0625…0.125)

各种优化都是可能的,特别是假设属性为
float
。但是对于最初的答案,我将坚持一种通用的方法

为了提高效率,循环只需从32向下迭代到
FLT\u MANT\u DIG
,通常为24

float ui32to0to1(uint32_t u) {
  float band = 1.0f/(1llu<<32);
  for (int i = 32; (i>FLT_MANT_DIG && ((u & 0x80000000) == 0)); i--) {
    u <<= 1;
    band *= 0.5f;
  }
  return (float)u * band;
}

要实现的是,虽然
(float)i
确实会损失8位精度(因此它有24位精度),但结果也只有24位精度。因此,这种精度损失不一定是坏事(这实际上更复杂,因为如果
i
更小,它将丢失不到8位。但事情会很顺利)

因此,我们只需要修正范围,使原来的非负值映射到
INT\u MIN..INT\u MAX

此表达式起作用:
(float)(int)(值^0x8000000)/0x8000000

下面是它的工作原理:

  • (int)(值^0x8000000)
    部分翻转符号位,因此
    0x0
    映射到
    int\u MIN
    ,而
    0xffffff
    映射到
    int\u MAX
  • 然后是到
    float
    的转换。这是一些舍入发生的地方,我们会失去精度(但这不是问题)
  • 然后只需除以
    0x8000000
    即可进入范围
    [-1..1]
    。由于此除法只调整指数部分,因此此除法不会失去任何精度
  • 因此,只有一个舍入操作,其他操作不会失去精度。这些操作链应该具有相同的效果,如以无限精度计算结果,然后对
    浮点值进行舍入(此理论舍入与2.步的舍入效果相同)


    但是,绝对可以肯定的是,我已经用蛮力检查了所有32位值,验证了这个表达式产生的值是否与
    (float)((double)value/0x8000000-1.0)相同

    可能
    float f=(float)((double)I/(1UI32在CUDA中,
    double
    上的操作速度太慢。很抱歉,我没有意识到我应该在问题中提到这一点。大多数情况下,将
    I
    转换为浮点值时会丢失精度。小的
    I
    值没有足够的精度丢失任何精度。对于足够大的va,可能会丢失一位
    i
    的LUE(也设置了低位)由于四舍五入的原因。
    double
    的性能不受CUDA的限制,而是受您运行的GPU的限制。一些支持CUDA的GPU比任何x86_64 CPU都具有更高的
    double
    吞吐量。如果您对尽可能快的性能感兴趣,我会尝试消除浮点除法运算。我认为这是可能的ble.取32位统一带符号整数。屏蔽低8位。结果是23位尾数和浮点数的符号位,指数计算起来很简单。
    i*ldexp(1.0,-32)
    is
    ldexp(i,-32)
    i*0x1p-32f
    。在C中,坚持
    float
    应该是
    ldexpf
    。类似地,
    1.0
    应该是
    1.0f
    。但是,在居中之前转换为
    float
    会丢失精度。OP可能在某种程度上希望这样做,但最好是在整数中减去,然后在整数中相乘
    float
    。但是在进行减法时会出现无符号/有符号问题。我认为这会在转换过程中损失很多精度…因为double没有理由使用结果的非规范化版本,而对float的转换很可能只是切断尾数位…导致大量整数值映射到同一个v但这只是我的猜测,我可能错了。多个整数值映射到同一个值是不可避免的
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    
    float ui32to0to1(uint32_t u) {
      if (u) {
        float band = 1.0f/(1llu<<32);
        while ((u & 0x80000000) == 0) {
          u <<= 1;
          band *= 0.5f;
        }
        return (float)u * band;
      }
      return 0.0f;
    }
    
    int test(uint32_t u) {
      volatile float f0 = (float) ((double)u / (1llu<<32));
      volatile float f1 = ui32to0to1(u);
      if (f0 != f1) {
        printf("%8lX %.7e %.7e\n", (unsigned long) u, f0, f1);
        return 1;
      }
      return 0;
    }
    
    int main(void) {
      for (int i=0; i<100000000; i++) {
        test(rand()*65535u ^ rand());
      }
      return 0;
    }
    
    float ui32to0to1(uint32_t u) {
      float band = 1.0f/(1llu<<32);
      for (int i = 32; (i>FLT_MANT_DIG && ((u & 0x80000000) == 0)); i--) {
        u <<= 1;
        band *= 0.5f;
      }
      return (float)u * band;
    }
    
    if (u >= 0x80000000) {
      return ui32to0to1((u - 0x80000000)*2);
    } else
      return -ui32to0to1((0x7FFFFFFF - u)*2);
    }