如何在C++;? 在C++标准库中,我只发现了浮点日志方法。现在我使用log在二叉树中查找索引的级别(floor(2log(index)))

如何在C++;? 在C++标准库中,我只发现了浮点日志方法。现在我使用log在二叉树中查找索引的级别(floor(2log(index))),c++,floating-accuracy,logarithm,C++,Floating Accuracy,Logarithm,代码(C++): 恐怕对于某些边缘元素(值为2^n的元素),log将返回n-1.9999999999,而不是n.0。这种恐惧正确吗?如何修改语句,使其始终返回正确答案?您可以使用此方法: int targetlevel = 0; while (index >>= 1) ++targetlevel; 注意:这将修改索引。如果需要保持不变,请创建另一个临时int 当索引为0时,角点情况为。如果索引=0,您可能应该单独检查它,并抛出异常或返回错误。您将树投影到多深?您可以设置一个范围,例

代码(C++):


恐怕对于某些边缘元素(值为2^n的元素),log将返回n-1.9999999999,而不是n.0。这种恐惧正确吗?如何修改语句,使其始终返回正确答案?

您可以使用此方法:

int targetlevel = 0;
while (index >>= 1) ++targetlevel;
注意:这将修改索引。如果需要保持不变,请创建另一个临时int


当索引为0时,角点情况为。如果索引=0,您可能应该单独检查它,并抛出异常或返回错误。

您将树投影到多深?您可以设置一个范围,例如…+/-将0.00000001设置为数字,以强制将其设置为整数值


实际上,我不确定您是否会遇到像1.9999999这样的数字,因为在计算2^n值时,您的log2不应该失去任何准确性(因为浮点舍入到最接近的2次方)。

如果您只需要快速整数log2运算,下面的函数
mylog2()
可以做到这一点,而不必担心浮点精度:

#include <limits.h>

static unsigned int mylog2 (unsigned int val) {
    if (val == 0) return UINT_MAX;
    if (val == 1) return 0;
    unsigned int ret = 0;
    while (val > 1) {
        val >>= 1;
        ret++;
    }
    return ret;
}

#include <stdio.h>

int main (void) {
    for (unsigned int i = 0; i < 20; i++)
        printf ("%u -> %u\n", i, mylog2(i));
    putchar ('\n');
    for (unsigned int i = 0; i < 10; i++)
        printf ("%u -> %u\n", i+UINT_MAX-9, mylog2(i+UINT_MAX-9));
    return 0;
}
对于输入值0,它将返回
UINT\u MAX
,作为未定义结果的指示,因此您应该检查这一点(没有有效的无符号整数的对数会那么高)


顺便说一下,有一些非常快的黑客可以做到这一点(在2的补码中找到最高的位集)。除非速度至关重要(我自己更喜欢可读性),否则我不建议使用它们,但你应该知道它们的存在。

我从未对你使用的公式的浮点精度有过任何问题(快速检查1到231-1之间的数字没有发现错误),但是如果你担心,你可以使用这个函数,它返回相同的结果,在我的测试中大约快66%:

int HighestBit(int i){
    if(i == 0)
        return -1;

    int bit = 31;
    if((i & 0xFFFFFF00) == 0){
        i <<= 24;
        bit = 7;
    }else if((i & 0xFFFF0000) == 0){
        i <<= 16;
        bit = 15;
    }else if((i & 0xFF000000) == 0){
        i <<= 8;
        bit = 23;
    }

    if((i & 0xF0000000) == 0){
        i <<= 4;
        bit -= 4;
    }

    while((i & 0x80000000) == 0){
        i <<= 1;
        bit--;
    }

    return bit; 
}
int最高位(int i){
如果(i==0)
返回-1;
int位=31;
如果((i&0xFFFF00)==0){

如果您在最近的ISH X86或X8664平台(您可能是),使用<代码> BSR 指令,它将返回无符号整数中最高设置位的位置,结果是与Log2()完全相同。这里是一个短的C或C++函数,使用内联ASM:< /P>调用<代码> BSR < /C> >
#include <stdint.h>
static inline uint32_t log2(const uint32_t x) {
  uint32_t y;
  asm ( "\tbsr %1, %0\n"
      : "=r"(y)
      : "r" (x)
  );
  return y;
}
#包括
静态内联uint32_t log2(常数uint32_t x){
uint32_t y;
asm(“\tbsr%1,%0\n”
:“=r”(y)
:“r”(x)
);
返回y;
}

这不是标准的,也不一定是可移植的,但它在一般情况下可以工作。我不知道它的效率有多高

将整数索引转换为足够精度的浮点数。如果精度足够,则表示形式将是精确的

查找IEEE浮点数的表示形式,提取指数,并进行必要的调整以找到基2日志。

这个函数是我编写的


此函数确定表示数值间隔[0..maxvalue]所需的位数

unsigned binary_depth( unsigned maxvalue )
   {
   int depth=0;
   while ( maxvalue ) maxvalue>>=1, depth++;
   return depth;
   }
通过从结果中减去1,可以得到
floor(log2(x))
,这是
log2(x)
的精确表示,当
x
是2的幂时

int targetIndex = floor(log(i + 0.5)/log(2.0));
xyy-1
00-1
110
221
321
432
532
632
732

843

上述评论中已提出这一点。使用gcc内置:

static inline int log2i(int x) {
    assert(x > 0);

    return sizeof(int) * 8 - __builtin_clz(x) - 1;
}

static void test_log2i(void) {
    assert_se(log2i(1) == 0);
    assert_se(log2i(2) == 1);
    assert_se(log2i(3) == 1);
    assert_se(log2i(4) == 2);
    assert_se(log2i(32) == 5);
    assert_se(log2i(33) == 5);
    assert_se(log2i(63) == 5);
    assert_se(log2i(INT_MAX) == sizeof(int)*8-2);
}
基2整数对数 下面是我对64位无符号整数所做的操作。这将计算以2为底的对数的下限,它相当于最高有效位的索引。此方法对于大数非常快,因为它使用了一个始终在日志中执行的展开循环₂64=6个步骤

本质上,它所做的是减去序列{0]中逐渐变小的正方形≤ K≤ 5:2^(2^k)}={2²,2М⁶, 2.⁸, 2.⁴, 2²,2ª}={429496729665536256,16,4,2,1}并将减去的值的指数k求和

int uint64_log2(uint64_t n)
{
  #define S(k) if (n >= (UINT64_C(1) << k)) { i += k; n >>= k; }

  int i = -(n == 0); S(32); S(16); S(8); S(4); S(2); S(1); return i;

  #undef S
}

如果您使用的是C++11,则可以将其设置为constexpr函数:

constexpr std::uint32_t log2(std::uint32_t n)
{
    return (n > 1) ? 1 + log2(n >> 1) : 0;
}

上面有类似的答案。这个答案

  • 适用于64位数字
  • 用于选择舍入和舍入的类型
  • 包括测试/示例代码
  • 职能:

        static int floorLog2(int64_t x)
        { 
          assert(x > 0);
          return 63 - __builtin_clzl(x);
        }
    
        static int ceilLog2(int64_t x)
        {
          if (x == 1)
            // On my system __builtin_clzl(0) returns 63.  64 would make more sense   
            // and would be more consistent.  According to stackoverflow this result  
            // can get even stranger and you should just avoid __builtin_clzl(0).     
            return 0;
          else
            return floorLog2(x-1) + 1;
        }
    
    测试代码:

    for (int i = 1; i < 35; i++)
      std::cout<<"floorLog2("<<i<<") = "<<floorLog2(i)
               <<", ceilLog2("<<i<<") = "<<ceilLog2(i)<<std::endl;
    
    for(int i=1;i<35;i++)
    
    std::cout改写托德·莱曼的答案,使之更具一般性:

    #include <climits>
    
    template<typename N>
    constexpr N ilog2(N n) {
        N i = 0;
        for (N k = sizeof(N) * CHAR_BIT; 0 < (k /= 2);) {
            if (n >= static_cast<N>(1) << k) { i += k; n >>= k; }
        }
        return i;
    }
    

    n
    为常数时,结果将在编译时计算。

    给定浮点数的工作方式(粗略地说,尾数*2^指数),则任何小于等于2^127的数字(即2的幂)都将准确无误地表示

    这确实给出了一个简单但相当粗糙的解决方案——将浮点数的位模式解释为整数,然后只看指数。这是上面David Thornley的解决方案

    float f = 1;
    for (int i = 0; i < 128; i++)
    {
        int x = (*(int*)(&f)>>23) - 127;
        int l = int(log(f) / log(2));
    
        printf("i = %d, log = %d, f = %f quick = %d\n",
            i, l, f, x);
        f *= 2;
    }
    
    float f=1;
    对于(int i=0;i<128;i++)
    {
    int x=(*(int*)(&f)>>23)-127;
    int l=int(log(f)/log(2));
    printf(“i=%d,log=%d,f=%f quick=%d\n”,
    i、 l,f,x);
    f*=2;
    }
    
    任何整数都不能表示为浮点数,只有位数小于尾数的整数才能表示。在32位浮点数中,值23位

    int log2(int x) {
        return sizeof(int)*8 - 1 - __builtin_clz(x);
    }
    

    假设你的x>0,从C++20开始,你可以使用

    std::bit_width(index) - 1
    
    非常简短、紧凑、快速且可读


    它遵循与相同的想法。

    我不明白这个问题。为什么它会返回n-1,9(9)?因为不是所有的整数都可以作为浮点数存储在中。如果7不合适,它将存储为7.000001或6.999999。是的,我知道。但是这1,9(9)从哪里来?也许你
    #include <climits>
    
    template<typename N>
    constexpr N ilog2(N n) {
        N i = 0;
        for (N k = sizeof(N) * CHAR_BIT; 0 < (k /= 2);) {
            if (n >= static_cast<N>(1) << k) { i += k; n >>= k; }
        }
        return i;
    }
    
    0000000100000f50    pushq   %rbp
    0000000100000f51    movq    %rsp, %rbp
    0000000100000f54    xorl    %eax, %eax
    0000000100000f56    cmpl    $0xffff, %edi
    0000000100000f5c    setg    %al
    0000000100000f5f    shll    $0x4, %eax
    0000000100000f62    movl    %eax, %ecx
    0000000100000f64    sarl    %cl, %edi
    0000000100000f66    xorl    %edx, %edx
    0000000100000f68    cmpl    $0xff, %edi
    0000000100000f6e    setg    %dl
    0000000100000f71    leal    (,%rdx,8), %ecx
    0000000100000f78    sarl    %cl, %edi
    0000000100000f7a    leal    (%rax,%rdx,8), %eax
    0000000100000f7d    xorl    %edx, %edx
    0000000100000f7f    cmpl    $0xf, %edi
    0000000100000f82    setg    %dl
    0000000100000f85    leal    (,%rdx,4), %ecx
    0000000100000f8c    sarl    %cl, %edi
    0000000100000f8e    leal    (%rax,%rdx,4), %eax
    0000000100000f91    xorl    %edx, %edx
    0000000100000f93    cmpl    $0x3, %edi
    0000000100000f96    setg    %dl
    0000000100000f99    leal    (%rdx,%rdx), %ecx
    0000000100000f9c    sarl    %cl, %edi
    0000000100000f9e    leal    (%rax,%rdx,2), %ecx
    0000000100000fa1    xorl    %eax, %eax
    0000000100000fa3    cmpl    $0x1, %edi
    0000000100000fa6    setg    %al
    0000000100000fa9    orl %ecx, %eax
    0000000100000fab    popq    %rbp
    
    float f = 1;
    for (int i = 0; i < 128; i++)
    {
        int x = (*(int*)(&f)>>23) - 127;
        int l = int(log(f) / log(2));
    
        printf("i = %d, log = %d, f = %f quick = %d\n",
            i, l, f, x);
        f *= 2;
    }
    
    int log2(int x) {
        return sizeof(int)*8 - 1 - __builtin_clz(x);
    }
    
    std::bit_width(index) - 1