Floating point 双精度小数表示法中的连续零数
在IEEE 754双精度数字的精确十进制表示中,连续非前导非尾随零(分别为9)的最大数量是多少 上下文 考虑将Floating point 双精度小数表示法中的连续零数,floating-point,ieee-754,Floating Point,Ieee 754,在IEEE 754双精度数字的精确十进制表示中,连续非前导非尾随零(分别为9)的最大数量是多少 上下文 考虑将double转换为十进制、向上舍入(或向下舍入)的问题,此时您能够使用的唯一原语是转换为最接近的现有函数(正确舍入到任何所需的位数) 你可以得到一些额外的数字,并删除他们自己。例如,要将1.875四舍五入到点后的一位数字,您可以将其转换为点后两位或三位最接近的十进制表示(1.87或1.875),然后自己擦除这些数字以获得预期的答案,1.8 对于某些数字和要打印的额外数字选择,此方法会产生
double
转换为十进制、向上舍入(或向下舍入)的问题,此时您能够使用的唯一原语是转换为最接近的现有函数(正确舍入到任何所需的位数)
你可以得到一些额外的数字,并删除他们自己。例如,要将1.875
四舍五入到点后的一位数字,您可以将其转换为点后两位或三位最接近的十进制表示(1.87
或1.875
),然后自己擦除这些数字以获得预期的答案,1.8
对于某些数字和要打印的额外数字选择,此方法会产生错误的结果。例如,对于最接近0.79999996
的double
,转换为十进制,四舍五入到最接近的2、3或4位,在点产生0.80
、0.800
和0.8000
。当所需结果为0.7
时,在转换后擦除附加数字会产生结果0.8
由于有有限数量的double,因此在初始转换中,为了在截断所获得的十进制表示后始终计算正确的结果,还存在足够数量的额外数字,可以打印这些数字。该数字与双精度
的精确十进制表示中可能出现的最大9或0数有关
相关的
这个问题与之相关,并且是双重的。显然,它至少是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
其中
- n>15
- A是整数
- 0
f=s*2^e
- s是整数
- 0
s=(A*2^n*5^n+B)*2^(p-e)*5^ps<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^p15满足b1的构造只会让事情变得更糟(它涉及减少a和B)
因此,我不认为有任何具有16个连续零的double,但这不是一个很好的演示…我没有解决方案,但我可以遵循以下方法:
- 跟踪到目前为止发现的最长的零字符串。调用长度
L
。它至少有15长,多亏了阿卡。尼斯的回答
- 对于每个可能的指数和每个位置
10^k
,其中L+1
可能出现连续的零,您会遇到一个凌乱的小背包问题
- 计算两个模
10^{k+L},
的相关53次幂,并对其进行四舍五入,使其具有约L+1
有效数字
- 使用本机整数数学查找两个模的前26次幂的所有组合
10^{k+L}
- 同样地,找到两个模的最后26次幂的所有组合
- 对两半进行排序,并使用线性扫描查找能提供非常接近隐含位负数的对。这样做可能只会得到几根火柴
- 使用
sprintf
或其他方法检查每个匹配项
看起来你必须运行这个循环几百万次,但它应该可以在几十个小时内在几十台现代计算机上运行
我还要补充一点,有一个整数可以用浮点表示,它有很多尾随零:879609302220800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000。(事实上,10^22完全可以表示为一个双精度数,它显然有22个尾随零。你做得再好不过了,因为5^23需要除以这样一个有效位,这是不可能的。哦,好吧。)实用的解决方案是:
- 调用现有函数返回一个字符串,该字符串只包含所需的小数位数李>
- 将结果转换回双精度值李>
- 如果该值大于原始值,则减小最后一位
[简短版本:答案是20。根据找到形式为2^e/10^d
的数字的良好有理近似值来重新计算问题;然后使用连分数为每个合适的d
和e
找到最佳近似值]
答案似乎是20
:也就是说,有一些IEEE 754二进制64浮点的示例,其十进制扩展具有20
连续零,但没有一个在其十进制扩展中具有21
连续零(不包括前导零和尾随零)。9的字符串也是如此
对于第一部分,我需要做的就是展示这样一个浮动。值0x1.9527560bfbed8p-1000
可精确表示为二进制64浮点,其十进制扩展包含20个零的字符串:
1.477012373908101575832232661339797638003193787888622256869396638475789157390440268509308176357891808688039997416681188265900445039128659150009310633265410967343958563709555236330766069662479012780743317388062800315618689682432778455224012594373130330432224324317331720251161832460427841947000000000000000000069852126871047947479
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
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)
0x1.9527560bfbed8p-1000 has 20 zeros in its expansion
0x1.fa712b8efae8ep-997 has 20 zeros in its expansion
0x1.515476ae79b24p-931 has 20 nines in its expansion
0x1.a5a9945a181edp-928 has 20 nines in its expansion
0x1.86049d3311305p-909 has 20 zeros in its expansion
0x1.69c08f3dd8742p-883 has 20 zeros in its expansion
0x1.1b41d80091820p-861 has 20 zeros in its expansion
0x1.62124e00b5e28p-858 has 20 zeros in its expansion
0x1.ba96e180e35b2p-855 has 20 zeros in its expansion
0x1.31c5be6377c48p-786 has 20 zeros in its expansion
0x1.7e372dfc55b5ap-783 has 20 zeros in its expansion
0x1.7e89dc1c3860ap-555 has 20 nines in its expansion
0x1.7e89dc1c3860ap-554 has 20 nines in its expansion
0x1.7e89dc1c3860ap-553 has 20 nines in its expansion
0x1.7e89dc1c3860ap-552 has 20 nines in its expansion
0x1.30bd91ea994cbp-548 has 20 zeros in its expansion
0x1.4a5f9de9ee064p-468 has 20 nines in its expansion
0x1.9cf785646987dp-465 has 20 nines in its expansion
0x1.c23142c9da581p-408 has 20 nines in its expansion
0x1.c23142c9da581p-407 has 20 nines in its expansion
0x1.c23142c9da581p-406 has 20 nines in its expansion
0x1.c23142c9da581p-405 has 20 nines in its expansion
0x1.ba431f4e34be9p+738 has 20 nines in its expansion
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;
}