C 除以64位整数,就像被除数左移64位一样,没有128位类型

C 除以64位整数,就像被除数左移64位一样,没有128位类型,c,integer-division,128-bit,int128,C,Integer Division,128 Bit,Int128,为这个令人困惑的标题道歉。我不知道如何更好地描述我正在努力实现的目标。我基本上是想做与之相反的事情 在C中,对于平台 int64_t divHi64(int64_t dividend, int64_t divisor) { return ((__int128)dividend << 64) / (__int128)divisor; } int64除数(int64除数,int64除数){ return((_int128)divident这可以在没有多字除法的情况下完成 假设我

为这个令人困惑的标题道歉。我不知道如何更好地描述我正在努力实现的目标。我基本上是想做与之相反的事情 在C中,对于平台

int64_t divHi64(int64_t dividend, int64_t divisor) {
    return ((__int128)dividend << 64) / (__int128)divisor;
}
int64除数(int64除数,int64除数){

return((_int128)divident这可以在没有多字除法的情况下完成

假设我们想这样做⌊264×x⁄y⌋ 然后我们可以像这样变换表达式

根据这个问题,第一个术语被简单地做为
(-y)/y+1)*x

第二项相当于(264%y)/y*x,有点复杂。我尝试过各种方法,但如果只使用整数运算,则都需要128位乘法和128/64位除法。这可以通过以下问题中的算法来计算
MulDiv64(a,b,c)=a*b/c

但是,它们可能很慢,如果您有这些函数,您可以更轻松地计算整个表达式,如
MulDiv64(x,UINT64_MAX,y)+x/y+某物,而不会弄乱上述转换

如果它具有64位或更高的精度,那么使用长双精度似乎是最简单的方法。因此,现在可以通过(264%y)/(长双精度)y*x来实现

为简化起见,省略了溢出检查。如果需要签名除法,则需要稍微修改


如果您的目标是64位Windows,但您使用的是没有
\uu int128
的MSVC,那么它在没有128位整数类型的情况下大大简化了作业。不过,您仍然需要处理溢出,因为在这种情况下会引发异常

uint64_t divHi64(uint64_t x, uint64_t y) {
    uint64_t high, remainder;
    uint64_t low = _umul128(UINT64_MAX, y, &high);
    if (x <= high /* && 0 <= low */)
        return _udiv128(x, 0, y, &remainder);
    // overflow case
    errno = EOVERFLOW;
    return 0;
}

当商不能用
int64\u t
表示时,您希望返回什么?另一个问题是两个64位操作数相乘,这可能需要128位来保存乘积。整数除法永远不会出现这种情况。@IanAbbott在这种情况下,如果结果超过64位,它可以简单地截断结果。是否使用MSVC,相关的:,你应该考虑使用一个任意精度的算术包,例如,这将给你一个干净和清晰的方式来做这项工作,以增加程序依赖性为代价。如果你想实现它自己,那么各种算法是已知的;维基百科提出。
uint64_t divHi64(uint64_t x, uint64_t y) {
    uint64_t high, remainder;
    uint64_t low = _umul128(UINT64_MAX, y, &high);
    if (x <= high /* && 0 <= low */)
        return _udiv128(x, 0, y, &remainder);
    // overflow case
    errno = EOVERFLOW;
    return 0;
}
#include <thread>
#include <iostream>
#include <limits>
#include <climits>
#include <mutex>

std::mutex print_mutex;

#define MAX_THREAD 8
#define NUM_BITS   27
#define CHUNK_SIZE (1ULL << NUM_BITS)

// typedef uint32_t T;
// typedef uint64_t T2;
// typedef double D;
typedef uint64_t T;
typedef unsigned __int128 T2;   // the type twice as wide as T
typedef long double D;
// typedef __float128 D;
const D epsilon = 1e-14;
T divHi(T x, T y) {
    T mod_y = std::numeric_limits<T>::max() % y + 1;
    T result = ((-y)/y + 1)*x;
    if (mod_y != y)
        result += (T)((mod_y/(D)y)*x + epsilon);
    return result;
}

void testdiv(T midpoint)
{
    T begin = midpoint - CHUNK_SIZE/2;
    T end   = midpoint + CHUNK_SIZE/2;
    for (T i = begin; i != end; i++)
    {
        T x = i & ((1 << NUM_BITS/2) - 1);
        T y = CHUNK_SIZE/2 - (i >> NUM_BITS/2);
        // if (y == 0)
            // continue;
        auto q1 = divHi(x, y);
        T2 q2 = ((T2)x << sizeof(T)*CHAR_BIT)/y;
        if (q2 != (T)q2)
        {
            // std::lock_guard<std::mutex> guard(print_mutex);
            // std::cout << "Overflowed: " << x << '&' << y << '\n';
            continue;
        }
        else if (q1 != q2)
        {
            std::lock_guard<std::mutex> guard(print_mutex);
            std::cout << x << '/' << y << ": " << q1 << " != " << (T)q2 << '\n';
        }
    }
    std::lock_guard<std::mutex> guard(print_mutex);
        std::cout << "Done testing [" << begin << ", " << end << "]\n";
}


uint16_t divHi16(uint32_t x, uint32_t y) {
    uint32_t mod_y = std::numeric_limits<uint16_t>::max() % y + 1;
    int result = ((((1U << 16) - y)/y) + 1)*x;
    if (mod_y != y)
        result += (mod_y/(double)y)*x;
    return result;
}

void testdiv16(uint32_t begin, uint32_t end)
{
    for (uint32_t i = begin; i != end; i++)
    {
        uint32_t y = i & 0xFFFF;
        if (y == 0)
            continue;
        uint32_t x = i & 0xFFFF0000;
        uint32_t q2 = x/y;
        if (q2 > 0xFFFF) // overflowed
            continue;
        
        uint16_t q1 = divHi16(x >> 16, y);
        if (q1 != q2)
        {
            std::lock_guard<std::mutex> guard(print_mutex);
            std::cout << x << '/' << y << ": " << q1 << " != " << q2 << '\n';
        }
    }
}

int main()
{
    std::thread t[MAX_THREAD];
    for (int i = 0; i < MAX_THREAD; i++)
        t[i] = std::thread(testdiv, std::numeric_limits<T>::max()/MAX_THREAD*i);
    for (int i = 0; i < MAX_THREAD; i++)
        t[i].join();
    
    std::thread t2[MAX_THREAD];
    constexpr uint32_t length = std::numeric_limits<uint32_t>::max()/MAX_THREAD;
    uint32_t begin, end = length;
    
    for (int i = 0; i < MAX_THREAD - 1; i++)
    {
        begin = end;
        end  += length;
        t2[i] = std::thread(testdiv16, begin, end);
    }
    t2[MAX_THREAD - 1] = std::thread(testdiv, end, UINT32_MAX);
    for (int i = 0; i < MAX_THREAD; i++)
        t2[i].join();
    std::cout << "Done\n";
}