我已经得到了四个unsigned32位整数表示未签名的128位整数,在little endian顺序:

typedef struct {
    unsigned int part[4];
} bigint_t;

我想把这个数字纳入其小数字符串的代表性和输出文件。

现在,我用一个 bigint_divmod10 功能划分的数量由10个,跟踪剩余部分。我呼吁这一功能的重复输的其余部分作为一位数,直到该数量为零。这是很缓慢的。这是最快的方式这样做?如果是这样,是否有一个聪明的方法来执行这一功能,我没看到?我已经试过寻找GMP的 get_str.c, 但我觉得它漂亮的坚不可摧的。

编辑:这是最快的代码我能想出的divmod10功能:

static unsigned uint128_divmod10(uint128 *value)
{
    unsigned int a = value->word[3];
    unsigned int b = value->word[2];
    unsigned int c = value->word[1];
    unsigned int d = value->word[0];

    unsigned int diva = a / 5;
    unsigned int divb = b / 5;
    unsigned int divc = c / 5;
    unsigned int divd = d / 5;

    value->word[3] = diva;
    value->word[2] = divb;
    value->word[1] = divc;
    value->word[0] = divd;

    unsigned int moda = a - diva*5;
    unsigned int modb = b - divb*5;
    unsigned int modc = c - divc*5;
    unsigned int modd = d - divd*5;

    unsigned int mod = 0;
    mod += moda;
    unsigned int carryb = mod*858993459;
    mod += modb;
    if (mod >= 5) {
        mod -= 5;
        carryb++;
    }
    unsigned int carryc = mod*858993459;
    mod += modc;
    if (mod >= 5) {
        mod -= 5;
        carryc++;
    }
    unsigned int carryd = mod*858993459;
    mod += modd;
    if (mod >= 5) {
        mod -= 5;
        carryd++;
    }

    uint128_add(value, carryd, 0);
    uint128_add(value, carryc, 1);
    uint128_add(value, carryb, 2);

    if (value->word[0] & 1) {
        mod += 5;
    }
    uint128_shift(value, -1);
    return mod;
}

在添加功能定义为:

static void uint128_add(uint128 *value, unsigned int k, unsigned int pos)
{
    unsigned int a = value->word[pos];
    value->word[pos] += k;
    if (value->word[pos] < a) {
        // overflow
        for (int i=pos+1; i<4; i++) {
            value->word[i]++;
            if (value->word[i]) {
                break;
            }
        }
    }
}
有帮助吗?

解决方案

这取决于你正在用数字做什么。你可以权衡的空间效率和多倍精度运算的效率,适度损失轻微损失,以换取非常有效的转换和小数。关键是要做到多倍精度运算用碱是10的幂而不是2的幂。

例如,您可以使用基地10,000,在那里你包一个数字为16位字,你做你的算术上的32位整数位。 (如果你是一个64位的机器上,你可以双击该做基础十亿。)这种代码是比较有效的时间上,虽然不太一样快,使用的两个原生动力,因为你不能利用在硬件上的进位。 你不能在相同的比特数代表尽可能多的整数。 但它在转换和从十进制高手,因为你到了个人数字转换没有任何长除法。

如果您需要从零代表的全系列数字来((1 << 128) - 1),你仍然可以做到这一点,但增加一个额外的数字,让您的数字就会越大。

如果事实证明你确实需要额外的空间/转速(也许你做了很多加密128位计算),那么点大DIV / MOD的10方法是我知道的最快方法。其他唯一的诀窍是,如果小整数是常见的,你可以专门处理它们。 (也就是说,如果三个最显著32位字都是零,只使用本机师进行转换。)

  

有没有办法实现这个功能,我没有看到一个聪明的办法?

戴夫Hanson的Ç接口和实现对多倍精度运算冗长章。由一个单一的数字除以大量是具有本有效的实现的特殊情况:

int XP_quotient(int n, T z, T x, int y) {
    int i;
    unsigned carry = 0;
    for (i = n - 1; i >= 0; i--) {
        carry = carry*BASE + x[i];
        z[i] = carry/y;
        carry %= y;
    }
    return carry;
}

有关充分的认识,它确实有助于有这本书,但的源代码仍然是容易得多比GNU源代码的理解。你可以很容易地适应它使用基地10,000(它目前使用的基体256)。

总结:如果你的性能瓶颈是转换为十进制,实施的多倍精度运算用碱即10 的功率。如果你的机器的本地字长为32,使用的是C代码,在16位字使用10,000。

其他提示

如果你的价值观大多小于ULLONG_MAX(18446744073709551615),我会尝试使用他们sprintf(buf,"%llu",ullong_val)。我打赌这是相当好于标准库优化,但格式的解析需要一定周期虽然。

否则,我创建bigint_divmod1000000000(或更好的名称mod10to9)函数,并使用它。这将需要比bigint_divmod10少9倍分歧。

查表的8位。你可以有4个查找表256号码。第一是从0到256LSB字节,第表是第一个表格乘以256等。

所以当你需要你的号码总数从查找表。当你加入,你可以添加如bunary去后通过每个字节的修复owerflows.

例 数0x12345678 在第一个查找表有下地址(0x78=120) 所以0x010200是第一个号码 在第二个表下(0x56=87)0x0202000106(0x56在dec是22016) 在第三表侯会有0x03040007080702 而在最后一个标在0x12你有0x030001090809080808(这不适合在32位的算术,但是你allredy知道)

然后总结一下这个数字(作为二进制bumbers)和去一个传,逐字节溢出 代码中为环喜欢的东西

s=carry+val[i];
val[i]=val[i]&10
carry=s/10; 
//you can put last two operations in table

如果我们计数的业务需要这一点。

1.(在表和加入)4个查找表。16补充(请记住,当你不需要携带大约owerflow,因为他们不能ocur)
2.一个通过在每个步骤3operatins16步骤,以通过。

passimistic上限6*16=100行动。

编辑:

这里是c++编码,并为30%,快于天真执行。

#include <iostream>
#include <stdint.h>
#include <array>

static uint64_t lu[4][256];

constexpr uint64_t lookup_value(uint64_t n) {
  uint64_t r = 0;
  uint64_t t = 1;
  while (n) {
    uint64_t rem = n % 10;
    n /= 10;
    r += rem * t;
    t *= 256;
  }
  return r;
}

void make_lu() {
  uint64_t step = 1;
  for (int j = 0; j < 4; ++j) {
    uint64_t n = 0;
    for (int i = 0; i < 256; ++i) {
      lu[j][i] = lookup_value(n);
      n += step;
    }
    step *= 256;
  }
}

struct DivMod {
  uint8_t div;
  uint8_t rem;
};

static DivMod dm[256];

void make_dm() {
  for (int i = 0; i < 256; ++i) {
    dm[i].div = i / 10;
    dm[i].rem = i % 10;
  }
}

void init() {
  make_lu();
  make_dm();
}

uint64_t b2d(uint64_t n) {
  uint64_t r = 0;
  for (int i = 0; i < 4; ++i) {
    r += lu[i][(n >> (i * 8)) & 0xff];
  }
  uint64_t r2 = 0;
  uint64_t of = 0;
  for (int i = 0; i < 8; ++i) {
    uint64_t v = ((r >> (i * 8)) & 0xff) + of;
    DivMod &x = dm[v];
    of = x.div;
    r2 += uint64_t(x.rem) << (i * 8);
  }
  return r2;
}

int main() {
  init();
  uint64_t n;
  std::cin >> n;
  std::cout << std::hex << b2d(n) << "\n";
  return 0;
}

有关将来参考,而不是实现uint128类型,我只是直接使用该字符串的字符。事实证明,这比从串去uint128和背部快得多。

的最直接的加速将来自内联的转换,而不是调用功能;它可以作为标记bigint_divmod10() 在线或使用由编译器提供档案导引优化的那样简单。

我知道这个问题是旧的,但我想因为没有放的方式避免了分裂周期做出贡献。这一次使用POW2,我还没有测试基准,但在理论上应该比其他任何更快,并且还可以在战俘功能进行调整为好。

#include <iostream>
#include <cmath>
using namespace std;

#define MathBintodec(arr,len)({int dec=0;int ci_;for(ci_=len;ci_--;)dec+=arr[ci_]*pow(2,len-ci_-1);dec;})

int main(){
    int r[]={1,0,0,1,0,0};
    cout<<MathBintodec(r,6)<<endl;
}

输出: 36

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top