Tho*_*mas 31 floating-point precision parsing
当然,大多数语言都有库函数,但我想自己想做.
假设浮点数是在C或Java程序中给出的(除了'f'或'd'后缀除外),例如" 4.2e1"," .42e2"或简单地" 42".通常,我们在小数点之前有"整数部分",在小数点之后有"小数部分"和"指数".这三个都是整数.
很容易找到并处理各个数字,但是如何将它们组合成类型的值float或double不丢失精度?
我想将整数部分乘以10 ^ n,其中n是小数部分中的位数,然后将小数部分添加到整数部分并从指数中减去n.例如,这有效地变成4.2e1了42e0.然后我可以使用该pow函数来计算10 ^ 指数并将结果与新的整数部分相乘.问题是,这种方法是否能保证最高精度?
有什么想法吗?
use*_*116 23
所有其他答案都错过了正确执行此操作的难度.您可以在此处进行第一次切割,这在某种程度上是准确的,但在您考虑IEEE舍入模式(等)之前,您将永远无法得到正确的答案.我之前写过一些天真的实现,但是有很多错误.
如果你不害怕数学的,我强烈建议您阅读大卫·戈德堡,下面的文章是什么每台计算机科学家应该知道关于浮点运算.您将更好地了解引擎盖下的内容,以及为什么这些内容都是如此布局的.
我最好的建议是从一个有效的atoi实现开始,然后从那里搬出去.你会发现很快你缺少的东西,但在几个长相的strtod的来源,你会在正确的道路上(这是一个很长很长的路径).最后,你会赞美插入diety有标准库.
/* use this to start your atof implementation */
/* atoi - christopher.watford@gmail.com */
/* 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);
}
Run Code Online (Sandbox Code Playgroud)
Pet*_*sel 19
用于将十进制数转换为最佳浮点近似的"标准"算法是William Clinger的如何准确读取浮点数,可从此处下载.请注意,正确执行此操作需要多个精度的整数,至少需要一定的时间百分比才能处理极端情况.
另一种方法,从浮动数字打印最佳十进制数,可以在Burger和Dybvig的快速准确的打印浮点数中找到,可在此下载.这也需要多精度整数运算
另请参阅David M Gay的正确舍入二进制 - 十进制和十进制 - 二进制转换,以实现双向算法.
Nil*_*nck 11
我会使用它的二进制表示直接组装浮点数.
读入一个接一个的字符,首先找到所有数字.在整数运算中执行此操作.还要跟踪小数点和指数.这个将在以后重要.
现在您可以组装浮点数.首先要做的是扫描第一组一位(从最高到最低)的数字的整数表示.
紧跟在第一位之后的位是你的尾数.
获得指数也不难.你知道第一个一位的位置,小数点的位置和科学记数法中的可选指数.合并它们并添加浮点指数偏差(我认为它是127,但请检查一些参考).
该指数应该在0到255范围内.如果它更大或更小,你有一个正或负无限数(特殊情况).
将指数存储到浮点数的24到30位.
最重要的一点就是标志.一个意味着消极,零意味着积极.
描述比实际更难,尝试分解浮点数并查看指数和尾数,你会看到它真的很容易.
顺便说一句 - 在浮点本身做算术是一个坏主意,因为你总是强迫你的尾数被截断为23个有效位.你不会那样得到精确的表达方式.