代码之家  ›  专栏  ›  技术社区  ›  Alecto Irene Perez

printf如何从浮点数中提取数字?

  •  7
  • Alecto Irene Perez  · 技术社区  · 7 年前

    如何实现以下功能 printf 从浮点数中提取数字?我知道这在原则上是怎么做到的。给定一个数 x ,其中你想要第一个 n 数字、刻度 以10的幂 介于 pow(10, n) pow(10, n-1) 是的。然后转换 变成一个整数,取整数的位数。

    我试过了,结果成功了。某种程度上。我的回答和 打印 对于前16个十进制数字,但之后的数字往往不同。怎样做 打印 做吗?

    3 回复  |  直到 7 年前
        1
  •  6
  •   Dietrich Epp    7 年前

    经典的实现是david gay的 dtoa 是的。确切的细节有些晦涩难懂(参见 Why does "dtoa.c" contain so much code? ,但通常情况下,它的工作原理是使用比32位、64位甚至80位浮点数更高的精度进行基转换。为此,它使用所谓的“bigint”或任意精度的数字,这些数字可以容纳尽可能多的内存。gay的代码经过修改被复制到无数其他库中,包括c标准库的通用实现(因此它可能会增强 printf ),java、python、php、javascript等。

    (作为旁注……并不是所有这些gay的dtoa代码副本都是最新的,所以因为php使用了 古老的 解析2.2250738585072011E-308时,strtod的版本挂起。)

    一般来说,如果你做一些“明显的”简单的事情,比如乘以10的幂,然后转换整数,你会损失少量的精度,一些结果会不准确……但也许你会得到正确的前14或15位数字。gay对dtoa()的实现声称要更正所有数字…但结果,代码很难遵循。跳到底部查看strtod本身,您可以看到它从一个“快速路径”开始,它只使用普通的浮点运算,但随后它会检测结果是否不正确,并使用一个更可靠的算法,该算法使用bigints,在所有情况下都有效(但速度较慢)。

    该实现包含以下引文,您可能会发现这些引文很有趣:

     * Inspired by "How to Print Floating-Point Numbers Accurately" by
     * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126].
    

    该算法通过计算产生给定二进制数的十进制数的范围来工作,并且通过使用更多的数字,范围会越来越小,直到您得到准确的结果或者您可以正确地舍入到所请求的位数为止。

    特别是,根据SEC 2.2算法,

    该算法使用精确的有理算法进行计算,这样就不会有精度损失。为了生成数字,该算法对数字进行缩放,使其形式为0.d 1个 2个 …,其中d 1个 ,天 2个 ,…,是B进制数字。第一个数字是通过将缩放的数字乘以输出基数b,并取整数部分来计算的。余数用于使用相同的方法计算其余数字。

    然后,该算法可以继续,直到得到精确的结果(这是始终可能的,因为浮点数是基数2,2是10的一个因数),或者直到它有尽可能多的数字。本文进一步证明了算法的正确性。


    还请注意,并非所有的 打印 基于gay的dtoa,这只是一个被大量复制的特别常见的实现。

        2
  •  2
  •   Eric Postpischil    7 年前

    有多种方法可以将浮点数无误地转换为十进制数(精确地转换或舍入到所需的精度)。

    一种方法是用小学教的算术。c提供处理浮点数的函数,例如 frexp ,它将分数(也称为有效位,通常错误地称为尾数)和指数分开。给定一个浮点数,可以创建一个大数组来存储十进制数字,然后计算这些数字。浮点数小数部分中的每一位表示两的幂,由浮点数中的指数决定。所以你可以简单地把1放在一个数字数组中,然后用小学的算术来乘或除它所需的次数。您可以对每个位执行此操作,然后将所有结果相加,求和就是等于浮点数的十进制数字。

    商业 printf 实现将使用更复杂的算法。讨论它们超出了堆栈溢出问题和答案的范围。这方面的开创性论文是 Correctly Rounded Binary-Decimal and Decimal-Binary Conversions by David M. Gay 是的。(副本似乎可用 here ,但这似乎是由第三方主办;我不确定它有多正式或持久。网络搜索可能会找到其他来源。)最近的一篇论文提出了一种将二进制浮点数转换为十进制数的算法,该算法具有唯一区分该值所需的最短位数 Printing Floating-Point Numbers: An Always Correct Method by Marc Andrysco, Ranjit Jhala, and Sorin Lerner 是的。

    如何做到这一点的关键是 打印 将不仅仅使用浮点格式及其操作来做这项工作。它将使用某种形式的扩展精度算法,或者使用整数格式的多个位的浮点数的一部分,或者将浮点数分成若干部分并使用多个浮点数来处理,或者使用精度更高的浮点格式。

    注意,问题的第一步,x乘以10的幂,已经有两个舍入误差。首先,并非所有的10次方都可以用二进制浮点表示,所以仅仅生成这样的10次方必然会有一些表示错误。然后,乘以 x 由另一个数产生的数学结果通常不能完全表示,因此必须四舍五入为浮点格式。

        3
  •  1
  •   klutt    7 年前

    无论是C还是C++标准都没有为这种情况指定特定的算法。因此不可能回答 printf 做这个。

    如果你想知道 一个例子 一个 打印 实现,您可以在这里查看: http://sourceware.org/git/?p=glibc.git;a=blob;f=stdio-common/vfprintf.c 在这里: http://sourceware.org/git/?p=glibc.git;a=blob;f=stdio-common/printf_fp.c