如何计算大数之间除法的第一个十进制数?

时间:2009-08-16 22:43:32

标签: c math integer-division

我有两个无符号长长的X和Y,其中X< Y,但两者都可能非常大。我想计算X / Y小数点后面的第一个数字。例如,如果X是11而Y是14,那么11/14是.785 ......,所以结果应该是7。

(X * 10)/ Y可以工作,但如果X * 10溢出则会产生错误的结果。如果我有理由相信它足够精确以计算正确的结果,那么转换为double就可以了。

这是在C.感谢您的帮助!

6 个答案:

答案 0 :(得分:4)

我不愿意通过浮点转换保证精确的结果,除非尾数有足够的位来准确表示所有的积分值。例如,tonsider y = 9223372036854775807并且x =(y div 10)-1 = 922337203685477579。其中“div”是整数除法。 x / y是0.09999999999999999981568563067746 ...,但是使用双精度可以获得> = 0.1。这是双重数据在有效数字中只有52位精度的结果(而y需要61位,x约为58)

您可以使用80位或128位FP精度,在这种情况下,您可以得到正确的答案,因为尾数将是> = 64位(ULL是64位,对吗?)因此您将无损地代表号。

我先从近似(使用整数或FP算术)开始,然后尝试乘法,看看答案是否应该少于1或更多。关键的见解是,只要您知道两个量之间的差异小于最大unsigned int的一半,您仍然可以比较两个可能溢出的整数。例如,这种比较技术是必要的。 TCP序列号溢出。

如果您只想使用 整数运算,则以下函数“fdd(x,y)”有效。我已经包含了一个main()来显示一些结果:

#include <iostream>
using namespace std;

typedef unsigned char ull; // change char to any integral type e.g. long long

const ull maxull=(ull)-1;
const ull halfull = maxull/2;
typedef unsigned long long asint;

// x = X mod (maxull+1), y= Y mod (maxull+1).  we only know x and y
// if we assume |X-Y|<halfull, then we return X<Y:
inline bool less_mod_near(ull x, ull y) {

    return (x<=halfull == y<=halfull) ? x<y : y>x;
}

// assuming x<y, return first decimal digit of 10x/y (return is in [0..9])
inline int fdd(ull x, ull y) { 
// assert(x<y);
 if (x<=maxull/10) return (10*x)/y; 
  // for speed, and to ensure that y>10 to avoid division by 0 later
 ull r=y/10;
 if (r*10==y) return x/r;
 ull ub=x/(r+1); // ub >= 10x div y (without overflow)
 ull x10=x*10; // allow overflow
 cout<<"ub="<<(asint)ub<<" x10="<<(asint)x10<<" r="<<(asint)r<<" ";
 return less_mod_near(x10,ub) ? ub-1 : ub; 
  // we already handled the 10 evenly divides y case
}

int pdd(ull x, ull y,ull mustbe)
{
    ull d=fdd(x,y);
    cout << (asint)x << '/' << (asint)y << " = ." << (asint)d << "...";
    if (d!=mustbe) cout << " (should be "<<(asint)mustbe<<")";
    cout<<endl;
//    assert(a==d);
}

int main() {
    pdd(0,1,0);
    pdd(1,2,5);
    pdd(11,101,1);
    pdd(10,101,0);
    pdd(49,69,7);
    pdd(50,69,7);
    pdd(48,69,6);
    pdd(160,200,8);
    pdd(161,200,8);
    pdd(159,200,7);
    pdd(254,255,9);
}

输出:

0/1 = .0...
1/2 = .5...
11/101 = .1...
10/101 = .0...
ub=7 x10=234 r=6 49/69 = .7...
ub=7 x10=244 r=6 50/69 = .7...
ub=6 x10=224 r=6 48/69 = .6...
160/200 = .8...
161/200 = .8...
159/200 = .7...
ub=9 x10=236 r=25 254/255 = .9...

答案 1 :(得分:3)

  

如果我有理由相信它足够精确以计算正确的结果,那么转换为double会有效。

如果你只需要第一个数字,那么肯定加倍是足够精确的。

编辑:评论中的反言示例证明我错了。

答案 2 :(得分:1)

转换为Double将在64分中的被除数和分母上放弃12位精度。大多数时候它会给你正确的答案,但它会出现舍入错误,除非它存储为usng 80位浮动格式。

编辑: 除非我太困了才能看到错误,否则我认为争吵的答案会起作用。 我早上上班,开车6个小时到客户的网站。 UGG。夜。

编辑: 最后一件事。 x86使用80位内部表示。我认为有些操作码会将int64转换为float80转换,如果你想抛出几个asm指令。与纯C实现相比,这将是一个更优雅,更快的解决方案,尽管它不可​​移植。

答案 3 :(得分:0)

X%Y给出余数R

然后,您可以从R和Y计算答案的小数部分

R / Y

要获得第一位数,请使用整数除法:

(long)((long)10*R/Y)

这应该向下舍入数字并删除任何额外的小数。

修改

为了匹配你的问题(对于那些想要挑剔的人)

Y % X = R

R / X

答案 4 :(得分:0)

怎么样

x / ((y + 9) / 10)

y + 9用于对分母中的商y / 10进行四舍五入,因此整体结果向下舍入。

对于大的x和y,它应该几乎总是正确的, 但它并不完美,它为你的例子11/14产生了5。

问题在于您因分割而丢失信息。由于乘以10已经溢出,除非使用更大数字的数据类型,否则无法解决这个问题。

答案 5 :(得分:-1)

如果你准备接受范围限制,你可以用整数表示 arithmatic: -

(X * 10)/ Y

在你的例子中:

(11 * 10) / 14

=&GT; 110/14

=&GT; 7

您的限制是将X的最大值减少了10倍。