从两个(64位)整数获得可靠的整数百分比

pf8*_*f85 4 c int 64-bit computer-science

在我的平台上,unsigned long long是64位(8字节).假设我有两个这样的变量:

unsigned long long partialSize;
unsigned long long totalSize;
//somehow determine partialSize and totalSize
Run Code Online (Sandbox Code Playgroud)

如何可靠地确定百分比(四舍五入到附近的整数)partialSizetotalSize多少?(如果可能的话,如果我不必假设前者小于后者,那将是很好的,但如果我真的必须做出这个假设,那就没关系.但我们当然可以假设两者都是非-负.)

例如,以下代码是完全防弹的吗?我担心它会包含某种舍入,转换或转换错误,这些错误可能导致比率在某些条件下失控.

unsigned long long ratioPercentage
    = (unsigned long long)( ((double)partialSize)/((double)totalSize) * 100.0 );
Run Code Online (Sandbox Code Playgroud)

Dan*_*her 5

它不是完全防弹的.doublemantissae只有53位(52 + 1隐式),所以如果你的数字大于2^53,转换到double通常会引入舍入错误.但是,与数字本身相关的舍入误差非常小,因此导致整数值的百分比计算将引入比转换更多的不准确性.

一个可能更严重的问题是,这将始终向下舍入,例如,对于totalSize = 1000partialSize = 99,它将返回9而不是更接近的值10.你可以通过0.5在转换之前添加更好的舍入unsigned long long.

只使用整数运算可以得到精确的结果(如果最终结果没有溢出),如果partialSize不是太大则相当容易:

if (partialSize <= ULLONG_MAX / 100) {
    unsigned long long a = partialSize * 100ULL;
    unsigned long long q = a / totalSize, r = a % totalSize;
    if (r == 0) return q;
    unsigned long long b = totalSize / r;
    switch(b) {
        case 1: return q+1;
        case 2: return totalSize % r ? q : q+1; // round half up
        default: return q;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你想要地板,天花板或圆形半对偶,可以轻松修改.

如果totalSize >= 100ULLONG_MAX / 100 >= partialSize % totalSize,没关系,

unsigned long long q0 = partialSize / totalSize;
unsigned long long r = partialSize % totalSize;
return 100*q0 + theAbove(r);
Run Code Online (Sandbox Code Playgroud)

在其他情况下,它变得更加繁琐,我并不热衷于这样做,但如果你需要,我可以被说服.