Floating point 双精度小数表示法中的连续零数

Floating point 双精度小数表示法中的连续零数,floating-point,ieee-754,Floating Point,Ieee 754,在IEEE 754双精度数字的精确十进制表示中,连续非前导非尾随零(分别为9)的最大数量是多少 上下文 考虑将double转换为十进制、向上舍入(或向下舍入)的问题,此时您能够使用的唯一原语是转换为最接近的现有函数(正确舍入到任何所需的位数) 你可以得到一些额外的数字,并删除他们自己。例如,要将1.875四舍五入到点后的一位数字,您可以将其转换为点后两位或三位最接近的十进制表示(1.87或1.875),然后自己擦除这些数字以获得预期的答案,1.8 对于某些数字和要打印的额外数字选择,此方法会产生

在IEEE 754双精度数字的精确十进制表示中,连续非前导非尾随零(分别为9)的最大数量是多少

上下文 考虑将
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^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^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;
    }