IEEE 754双精度数的精确十进制表示中连续的非前导非尾随零(相应的nines)的最大数量是多少?
考虑将double
转换为十进制,向上舍入(分别向下)的问题,当您能够使用的唯一原语是转换为最接近的现有函数时(正确舍入到任何所需的数字)数字)。
您可以获得一些额外的数字并自行删除它们。例如,要将点1.875
向下舍入到一位数后,您可以将其转换为距离点(1.87
或1.875
)之后的两位或三位数的最近的十进制表示形式,然后自己删除数字以获得预期答案1.8
。
对于某些数字和要打印的其他位数的选择,此方法会产生错误的结果。例如,对于距离double
最近的0.799999996
,转换为十进制,在点后生成0.80
,0.800
并舍入到最近的,2,3或4位数, 0.8000
。当期望的结果为0.8
时,在转换后删除其他数字会生成结果0.7
。
存在有限数量的double
,存在许多额外的数字,它们足以在初始转换中打印,以便在截断所获得的十进制表示后始终计算正确的结果。此数字与double
的精确十进制表示中可能出现的最大九或零数相关。
此问题与this question about rounding down in the conversion of a double
to decimal有关,并且是this question about the correctly rounded conversion of decimal representations to doubles的双重问题。
答案 0 :(得分:9)
[简短版本:答案是20.重新解决问题,找到2^e / 10^d
形式的数字的良好理性近似值;然后使用连续分数为每个合适的d
和e
找到最佳近似值。]
答案似乎是20
:也就是说,有一些IEEE 754 binary64浮点数的示例,其十进制扩展具有20
个连续的零,但没有21
个连续的零它们的十进制扩展(不包括前导和尾随零)。对于九个字符串也是如此。
对于第一部分,我需要做的就是展示这样的浮动。值0x1.9527560bfbed8p-1000
可以完全表示为binary64 float,其十进制扩展包含20个零的字符串:
1.4770123739081015758322326613397693800319378788862225686396638475789157389044026850930817635789180868803699741668118826590044503912865915000931065333265410967343958956370955236330760696646247901278074331738806828003156818618589682432778455224012594723731303304343292224317331720902511661748324604219378419442700000000000000000000740694966568985212687104794747958616712153948337746429554804241586090095019654323133732729258896166004754316995632195371041441104566613036026346868128222593894931067078171989365490315525401375255259854894072456336393577718955037826961967325532389800834191597056333066925969522850884268136311674777047673845172073566950098844307658716553833345849153012040436628485227928616281881622762650607683099224232137203216552734375E-301
关于9的问题部分,0x1.c23142c9da581p-405
的十进制扩展包含20个9的字符串:
2.12818792307269553358078502102171540639252016258831784842556110831434197718043638405555406495645619729155240037555858106390933161420388023706431461384056688295540725831155392678607931808851292893574214797681879999999999999999999941026584542575391157788777223962620780080784703190447744595561259568772261019375946489162743091583251953125E-122
为了解释我如何找到上面的数字,并表明没有21个连续零的例子,我们需要更加努力。对于某些整数(a + eps)*10^d
和a
以及实数d
,eps
格式为a
,其实数为{9}或0非零(我们可能假设a
为正)和eps
非零且小。例如,如果0 < abs(eps) < 10^-10
,则a + eps
小数点后面至少有10个零(如果eps
为正数),或者小数点后面有10个零(如果eps
为{负);乘以10^d
可以移动零或九的字符串的位置。
但是我们对上述形式的数字感兴趣,这些数字同时可以表示为IEEE 754 binary64 float;换句话说,对于整数b*2^e
和b
满足e
的{{1}}形式的2^52 <= b <= 2^53
形式的数字,其中e
的范围有限(并且还有一些额外的一旦我们进入低于正常范围,b
的限制,但我们可以稍后担心。
结合这一点,我们正在寻找整数(a + eps) * 10^d = b * 2^e
,a
,b
和d
中e
的解决方案,以便eps
1}}很小,a
是正数且2^52 <= b <= 2^53
(我们后来会担心d
和e
的范围。重新排列,我们得到eps / b = 2^e / 10^d - a / b
。换句话说,我们正在寻找具有有限分母的2^e / 10^d
的良好有理近似。这是连续分数的经典应用:给定d
和e
,可以有效地找到具有2^53
限制的分母的最佳有理逼近。
所以解决方案策略一般是:
for each appropriate d and e:
find the best rational approximation a / b to 2^e / 10^d with denominator <= 2^53
if (the error in this rational approximation is small enough):
# we've got a candidate
examine the decimal expansion of b*2^e
我们只有大约2千个值用于检查,最差的是每个这样的e几百d,因此整个计算上非常可行。
现在详细说明:&#34;足够小&#34;意思?哪个d
和e
是&#34;适当的&#34;?
至于&#34;足够小&#34;:让我们说我们正在寻找至少19个零或九的字符串,所以我们正在寻找{{{{ 1}}。因此,对于每个0 < abs(eps) <= 10^-19
和d
,所有e
和a
都可以找到b
。请注意,由于abs(2^e / 10^d - a / b) <= 10^-19 * 2^-52
的限制,只能有一个这样的分数b
;如果还有另一个a / b
,那么我们就会a' / b'
,这是一个矛盾。因此,如果存在这样的分数,它必然是给定分母界限的最佳有理逼近。
对于1 / 2^106 <= 1 / (b *b') <= abs(a / b - a' / b') <= 2 * 10^-19 * 2^-52
和d
:要涵盖包含次正规的binary64范围,我们希望e
的范围从e
到-1126
。如果971
过大,d
将远小于2^e / 10^d
,并且没有解决方案的希望; 2^-53
是一个实际的约束。如果d <= 16 + floor(e*log10(2))
太小(或太负),那么d
将是一个整数,并且没有解决方案;为避免这种情况,我们需要2^e / 10^d
。
考虑到所有内容,让我们编写一些代码。 Python解决方案非常简单,部分归功于Fraction.limit_deminator方法的存在,它完全可以在极限范围内找到最佳有理逼近。
d > min(e, 0)
那里有足够的性能改进空间(不使用Python&#39; from fractions import Fraction
from itertools import groupby
from math import floor, log10
def longest_run(s, c):
"""Length of the longest run of a given character c in the string s."""
runs = [list(g) for v, g in groupby(s, lambda k: k == c) if v]
return max(len(run) for run in runs) if runs else 0
def closest_fraction(d, e):
"""Closest rational to 2**e/10**d with denominator at most 2**53."""
f = Fraction(2**max(e-d, 0) * 5**max(-d, 0), 2**max(0, d-e) * 5**max(0, d))
approx = f.limit_denominator(2**53)
return approx.numerator, approx.denominator
seen = set()
emin = -1126
emax = 971
for e in range(emin, emax+1):
dmin = min(e, 0) + 1
dmax = int(floor(e*log10(2))) + 16
for d in range(dmin, dmax+1):
num, den = closest_fraction(d, e)
x = float.fromhex('0x{:x}p{}'.format(den, e))
# Avoid duplicates.
if x in seen:
continue
seen.add(x)
digits = '{:.1000e}'.format(x).split('e')[0].replace('.','').strip('0')
zero_run = longest_run(digits, '0')
if zero_run >= 20:
print "{} has {} zeros in its expansion".format(x.hex(), zero_run)
nine_run = longest_run(digits, '9')
if nine_run >= 20:
print "{} has {} nines in its expansion".format(x.hex(), nine_run)
模块将是一个良好的开端:-);就目前而言,需要几分钟才能完成。以下是结果:
fractions
答案 1 :(得分:1)
显然,它至少是15,如Smalltalk / Squeak代码所示:
1.0 successor asTrueFraction printShowingMaxDecimalPlaces: 100.
-> '1.0000000000000002220446049250313080847263336181640625'
1.0 predecessor asTrueFraction printShowingMaxDecimalPlaces: 100.
-> '0.99999999999999988897769753748434595763683319091796875'
现在,它更多地涉及到证明不能超过15个连续的零。您搜索浮点数f=(A*10^n+B)*10^p
但浮点数也必须表示为整数有效数和偏差指数f=s*2^e
其中
我们因此s=(A*2^n*5^n+B)*2^(p-e)*5^p
与s < 2^53
。
我的第一个回答是假的,这是要完成的......
一般来说,s可以写成,s = 2 ^ a * 5 ^ b + c,c不能被2和5整除。
A * 2 ^(n + p-e)* 5 ^(n + p)+ B * 2 ^(p-e)* 5 ^ p = 2 ^ a * 5 ^ b + c
我们可以搜索A = 1的构造,B = c * 2 ^(e-p)/ 5 ^ p <1,n + p-e = a,n + p = b,e-p = n-a。
B = C * 2 ^(N-A)/ 5 ^(B-N)
我尝试了所有对a,b,这样2 ^ 52&lt; 2 ^ a * 5 ^ b&lt; 2 ^ 53,但是找不到满足B&lt; 1的任何n> 15 ...尝试用A&gt; 1只会使事情变得更糟(它涉及减少a和b)。
因此,我不认为有任何双重连续16个零,但它不是一个美丽的示范...
答案 2 :(得分:1)
我没有这方面的解决方案,但这是我可能遵循的方法:
L
。由于aka.nice的答案,它至少有15个长。10^k
,其中L+1
连续零可能发生,你会得到一个凌乱的小背包问题。10^{k+L},
的相关53次幂并围绕它们,使它们有大约L+1
个有效数字。10^{k+L}
的前26个幂的所有组合。10^{k+L}
的最后26个幂的所有组合。sprintf
或其他内容检查每场比赛。好像你必须运行这个循环几百万次,但它应该可以在几十个小时内在几十台现代计算机上运行。
我还要补充说,在浮点中有一个完全可以表示的整数,它有很多尾随零:87960930222080000000000000000000000
有22个尾随零,并且是10F0CF064DD5920000000000000000
十六进制。 (事实上,10 ^ 22可以完全表示为双,它显然有22个尾随零。你不能做得更好,因为5 ^ 23需要划分这样的有效数,这是不可能的。哦,好吧。)
答案 3 :(得分:0)
此代码将在十进制结束时删除0。
private void button1_Click(object sender, EventArgs e)
{
String Str = textBox1.Text;
String lstVal = GetDecimalString(Str);
textBox2.Text = lstVal;
}
private static string GetDecimalString(String Str)
{
String lstVal = "";
if (Str.IndexOf(".") > -1)
{
String[] Last = Str.Split('.');
if (Last.Length == 1)
{
lstVal = Last[0];
}
else
{
lstVal = Last[1];
}
String TrimedData = lstVal;
for (int i = lstVal.Length - 1; i >= 0; i--)
{
if (TrimedData.EndsWith("0") && TrimedData.Length > 3)
{
TrimedData = TrimedData.Substring(0, i - 1);
}
}
lstVal = TrimedData;
if (lstVal.Length < 3)
lstVal = lstVal.PadRight(3, '0');
lstVal = String.Join(".", Last[0], lstVal);
}
else
{
lstVal = Str;
}
return lstVal;
}
答案 4 :(得分:-2)
务实的解决方案是: