我有两个无符号长长的X和Y,其中X< Y,但两者都可能非常大。我想计算X / Y小数点后面的第一个数字。例如,如果X是11而Y是14,那么11/14是.785 ......,所以结果应该是7。
(X * 10)/ Y可以工作,但如果X * 10溢出则会产生错误的结果。如果我有理由相信它足够精确以计算正确的结果,那么转换为double就可以了。
这是在C.感谢您的帮助!
答案 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倍。