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

Posted

技术标签:

【中文标题】如何计算大数之间除法的第一个十进制数字?【英文标题】:How do I compute the first decimal digit of a division between large numbers? 【发布时间】:2009-08-16 22:43:31 【问题描述】:

我有两个无符号长整数 X 和 Y,其中 X

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

这是 C 语言。感谢您的帮助!

【问题讨论】:

【参考方案1】:

我不愿意通过浮点转换来保证准确的结果,除非尾数有足够的位来准确表示所有整数值。例如,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...

【讨论】:

【参考方案2】:

如果我有理由相信它足够精确以计算正确的结果,则转换为双精度值。

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

编辑: wrang-wrang 在 cmets 中的反例证明我错了。

【讨论】:

这是错误的。考虑 y=9223372036854775807 和 x = (y div 10) - 1 = 922337203685477579。其中“div”是整数除法。 x/y 是 0.09999999999999999981568563067746,但使用双打会给你 >= 0.1,而不是 我们想要多少精度? 0.09(50 more 9s)5 真的是 0.1,还是算作 0.0?在某些时候,我们必须停止计数并将其四舍五入到收盘价。如果我们关心精确精度,那么答案将是使用具有可变精度的数学库,我们可以停在那里并不再担心位数 @shimpossible:这取决于。如果他只得到第一个数字,也许这没什么大不了的。但如果他要取出前 5 个左右,那么 .899999 与将第一个数字四舍五入为 0.9 可能会搞砸其余的转换。 “我想计算小数点后的第一个数字”我认为这仅表示第一个数字。仍然没有告诉我有多准确.. 如果第一个数字是 0 并且程序输出 1,即使使用最弱的合理准确度衡量标准,答案也是 100%。【参考方案3】:

转换为 Double 将放弃 64 位中除数和施主的 12 位精度。大多数情况下,它会为您提供正确的答案,但它偶尔会出现舍入错误,除非它是使用存储的80bit 浮动格式。

编辑: 除非我太困而无法看到错误,否则我认为 wrang-wrang 的答案会起作用。 我早上上班,开车6小时到客户现场。乌格。晚上。

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

【讨论】:

【参考方案4】:

X % Y 给出余数 R

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

R / Y

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

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

这应该将数字四舍五入并去掉任何多余的小数。

编辑:

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

Y % X = R

R / X

【讨论】:

所以翻转它...我向后输入。 以问题中的 11/14 为例:14 % 11 = 3。所以3*10/11 = 2 这是不正确的。这是Y/X 的结果的第一个十进制数字,而不是X/Y。 14/11 = 1.2727... 依赖可分性,正确的结果是10 - R/X9 - R/X【参考方案5】:

怎么样

x / ((y + 9) / 10)

y + 9 用于将分母中的商 y/10 向上取整,因此整体结果向下取整。

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

问题是你会因为分裂而丢失信息。由于乘以 10 已经溢出,除非使用更大的数字数据类型,否则无法解决。

【讨论】:

【参考方案6】:

如果你准备好接受范围限制,你可以用整数来做 算术:-

(X * 10) / Y

在你的例子中:

(11 * 10) / 14

=> 110 / 14

=> 7

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

【讨论】:

以上是关于如何计算大数之间除法的第一个十进制数字?的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 405. 数字转换为十六进制数(补码的问题) / 166. 分数到小数(模拟长除法) / 482. 密钥格式化

C语言中如何实现大数计算

严格使用乘法和除法在Java中将二进制数转换为十进制数

实验九

[CODEUP] 1943 进制转换

数制转换