如何从字符串中手动解析浮点数

时间:2020-03-05 18:59:23  来源:igfitidea点击:

当然,大多数语言对此都有库函数,但是假设我想自己做。

假设以C或者Java程序的形式给出浮点数(" f"或者" d"后缀除外),例如" 4.2e1","。42e2"或者" 42"。通常,我们在小数点前有"整数部分",在小数点后有"小数部分",以及"指数"。这三个都是整数。

查找和处理单个数字很容易,但是如何在不损失精度的情况下将它们组合为" float"或者" double"类型的值呢?

我正在考虑将整数部分与10 ^ n相乘,其中n是小数部分中的位数,然后将小数部分加到整数部分,然后从指数中减去n。例如,这有效地将" 4.2e1"转换为" 42e0"。然后我可以使用pow函数来计算10 ^指数,然后将结果与新的整数部分相乘。问题是,这种方法是否始终保证最高的精度?

有什么想法吗?

解决方案

回答

使用状态机。这很容易做到,甚至在数据流中断的情况下也可以工作(我们只需要保留状态和部分结果即可)。我们还可以使用解析器生成器(如果我们要执行更复杂的操作)。

回答

为此,我们必须了解标准IEEE 754才能正确地进行二进制表示。之后,我们可以使用Float.intBitsToFloat或者Double.longBitsToDouble。

http://en.wikipedia.org/wiki/IEEE_754

回答

如果希望获得最精确的结果,则应使用较高的内部工作精度,然后将结果下转换为所需的精度。如果我们不介意一些错误的ULP,则可以根据需要以所需的精度重复乘以10. 我会避免使用pow()函数,因为它将对大指数产生不精确的结果。

回答

我将使用其二进制表示形式直接汇编浮点数。

依次读入一个数字,然后首先找到所有数字。用整数算术做到这一点。还要跟踪小数点和指数。稍后,这一点很重要。

现在,我们可以汇编浮点数了。首先要做的是扫描数字的整数表示形式,以找到第一个设置的一位(最高到最低)。

第一个后跟的位是尾数。

获得指数也不难。我们可以从科学计数法中知道第一个位的位置,小数点的位置和可选的指数。合并它们并添加浮点指数偏差(我认为是127,但请检查一些参考)。

该指数应在0到255的范围内。如果它更大或者更小,则表示正数或者负数为无穷大(特殊情况)。

将指数存储在浮点数的第24至30位中。

最重要的一点就是符号。一表示负数,零表示正数。

很难描述比实际要复杂的数,尝试分解浮点数并查看指数和尾数,我们会发现它实际上是多么容易。

顺便说一下,在浮点数中进行算术运算本身不是一个好主意,因为我们将始终迫使尾数被截断为23个有效位。这样我们将无法获得确切的表示。

回答

我们可以在分析时忽略小数点(位置除外)。说输入是:
156.7834e10 ...可以很容易地将其解析为整数1567834,后跟e10,然后我们将其修改为e6,因为小数点是浮点数"数字"部分末尾的4位数字。

精度是一个问题。我们需要检查所用语言的IEEE规范。如果尾数(或者分数)中的位数大于整数类型中的位数,那么当有人键入以下数字时,我们可能会失去精度:

5123.123123e0在我们的方法中转换为5123123123,这不适合整数,但是5.123123123的位可能适合于float规范的尾数。

当然,我们可以使用以下方法:将小数点前的每个数字都乘以10,然后将当前总数(以浮点数)乘以10,然后添加新数字。对于小数点后的数字,将数字乘以10的递增幂,然后再添加到当前总数中。但是,此方法似乎引出了我们为什么要这样做的问题,因为它需要使用浮点基元而不使用随时可用的解析库。

无论如何,祝你好运!

回答

在不损失精度的情况下,不可能将代表数字的任意字符串转换为双精度或者浮点型。有许多小数可以精确地用十进制表示(例如" 0.1"),只能用二进制浮点数或者双精度数近似。这类似于分数1/3不能精确地用十进制表示的方式,我们只能写0.333333 ...

如果我们不想直接使用库函数,为什么不查看这些库函数的源代码?我们提到Java;大多数JDK附带了类库的源代码,因此我们可以查看java.lang.Double.parseDouble(String)方法的工作方式。当然,像BigDecimal这样的东西更适合控制精度和舍入模式,但是我们说过它必须为float或者double。

回答

(编辑:在David Goldberg的文章上增加了一点)

所有其他答案都错过了正确执行此操作的难度。我们可以在某种程度上做到这一点,这在某种程度上是准确的,但是直到我们考虑到IEEE舍入模式(et al)之后,我们才会找到正确的答案。我之前写过一些幼稚的实现,但有很多错误。

如果我们不害怕数学,我强烈建议我们阅读David Goldberg的以下文章,《每位计算机科学家应该了解的浮点算术》。我们将更好地了解引擎盖下发生的事情以及这些位为何如此布置。

我最好的建议是从可行的atoi实施开始,然后从那里实施。我们会迅速发现自己缺少的东西,但是有一些人看着strtod的来源,我们将走在正确的道路上(这是一条漫长的道路)。最终,我们会赞叹这里插入了Diety,因为这里有标准库。

/* use this to start your atof implementation */

/* atoi - [email protected] */
/* PUBLIC DOMAIN */
long atoi(const char *value) {
  unsigned long ival = 0, c, n = 1, i = 0, oval;
  for( ; c = value[i]; ++i) /* chomp leading spaces */
    if(!isspace(c)) break;
  if(c == '-' || c == '+') { /* chomp sign */
    n = (c != '-' ? n : -1);
    i++;
  }
  while(c = value[i++]) { /* parse number */
    if(!isdigit(c)) return 0;
    ival = (ival * 10) + (c - '0'); /* mult/accum */
    if((n > 0 && ival > LONG_MAX)
    || (n < 0 && ival > (LONG_MAX + 1UL))) {
      /* report overflow/underflow */
      errno = ERANGE;
      return (n > 0 ? LONG_MAX : LONG_MIN);
    }
  }
  return (n>0 ? (long)ival : -(long)ival);
}

回答

将小数转换为最佳浮点近似值的"标准"算法是William Clinger的"如何准确读取浮点数",可从此处下载。请注意,正确地执行此操作需要至少在一定百分比的时间中使用多个精度整数,以便处理极端情况。

在Burger和Dybvig的《快速,准确地打印浮点数》中可以找到从浮点数打印最佳十进制数的另一种算法,可在此处下载。这也需要多精度整数运算

另请参见David M Gay的正确舍入的二进制-十进制和十进制-二进制转换,以了解双向算法。

回答

我同意终点站。状态机是完成此任务的最佳方法,因为解析器有很多愚蠢的方法可以被破坏。我现在正在研究一个,我认为它已经完成,并且我认为它有13个州。

这个问题并非微不足道。

我是一位对设计浮点硬件感兴趣的硬件工程师。我正在第二次实施。

我今天发现了这个http://speleotrove.com/decimal/decarith.pdf

在第18页上给出了一些有趣的测试用例。

是的,我已经阅读了Clinger的文章,但是作为一名简单的硬件工程师,我无法理解所提供的代码。 Knuth课文中提到的对Steele算法的引用对我很有帮助。输入和输出都是有问题的。

前面提到的各种文章的参考文献都很出色。

我还没有在这里注册,但是当我这样做的时候,假设没有登录,那就很麻烦了。 (broh-dot)。

克莱德