How do I compute the first decimal digit of a division between large numbers?
我有两个无符号的长整型X和Y,其中X (X * 10)/ Y将起作用,但是如果X * 10溢出则会产生错误的结果。如果我有理由相信它足够精确,可以计算出正确的结果,则转换为double会有用。 这是C语言。谢谢您的帮助! 我不愿意通过浮点转换来保证准确的结果,除非尾数具有足够的位来精确表示所有整数值。例如,扁桃体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。关键的见解是,只要您知道两个量之间的差小于最大无符号整数的一半,您仍然可以比较两个可能溢出的整数。例如,当TCP序列号溢出。 如果只想使用整数运算,则下面的函数" fdd(x,y)"有效。我包含了main()来显示一些结果: 输出:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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);
}
2
3
4
5
6
7
8
9
10
11
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...
Conversion to double would work if I had reason to believe it is precise enough to compute the right result.
如果只需要第一位数字,那么肯定会精确到两倍。
编辑:评论中wrang-wrang \\的反例证明我错了。
转换为Double将使64位中的红利和支配者两者都放弃12位精度。在大多数情况下,它会给您正确的答案,但是,除非有舍入错误,否则有时会出现四舍五入的错误。使用80位浮点格式存储的
编辑:
除非我太困了才能看到错误,否则我认为wrang-wrang \\的答案会起作用。
我早上上班,开车到客户现场需要6个小时。哎呀晚上。
编辑:
最后一件事。 x86使用80位内部表示。我认为,如果您想折腾一些asm指令,会有一些操作码可以将int64转换为float80。与纯C实现相比,这将是一个更优雅,当然更快的解决方案,尽管它不是可移植的。
怎么样
1 | x / ((y + 9) / 10) |
y 9用于对分母中的y / 10进行四舍五入,因此将总结果四舍五入。
对于大的x和y,它几乎总是正确的,
但这并不完美,示例11/14会产生5。
问题是由于划分,您失去了信息。由于乘以十已经溢出,因此除非使用较大的数字数据类型,否则您将无法解决。
X%Y给出余数R
然后您可以根据R和Y计算答案的小数部分。
1 | R / Y |
要获取第一个数字,请使用整数除法:
1 | (long)((long)10*R/Y) |
这应该四舍五入数字并删除任何多余的小数。
编辑:
为了匹配您的问题(对于那些想挑剔的人),其
1 | Y % X = R |
和
1 | R / X |
如果您愿意接受范围限制,则可以整数形式完成所有操作
算术的:-
(X * 10)/ Y
在您的示例中:
1 | (11 * 10) / 14 |
=> 110/14
=> 7
限制是您已将X的最大值减少了10倍。