我需要使用以下结构表示数字。这种结构的目的不是要失去精确度。
struct PreciseNumber
{
long significand;
int exponent;
}
使用此结构,实际的double值可以表示为value = significand * 10e^exponent
。
现在我需要编写实用函数,它可以将两个转换为PreciseNumber。
你能告诉我如何从双重中提取指数和有效数字吗?
答案 0 :(得分:1)
前奏有些缺陷。
首先,除非对存储空间有任何限制,否则从双精度转换为基数10有效指数形式不会改变任何形式的精度。要理解这一点,请考虑以下内容:任何二进制终止分数(如在典型的IEEE-754浮点上形成尾数的分数)都可以写为负2的负和。 2的每个负幂都是终止分数本身,因此它的总和也必须终止。
然而,反过来并不一定如此。例如,0.3
基数10相当于基数2中的非终止0.01 0011 0011 0011 ...
。将其拟合为固定大小的尾数会使其中出现一些精度(这就是为什么0.3
是实际上存储为转换回0.29999999999999999
的东西。)
通过这种方式,我们可以假设通过以十进制有效数和指数形式存储数字而预期的任何精度要么丢失,要么根本不会获得。
当然,您可能会想到将十进制数作为浮点数存储为精度损失而产生的精度明显下降,在这种情况下,Decimal32和Decimal64浮点格式可能会引起一些兴趣 - 请查看{{ 3}}
答案 1 :(得分:0)
这是一个非常棘手的问题。您可能希望查看实现双字符串转换所需的代码量(例如,对于printf)。您可能会从gnu的gcc实现中窃取代码。
答案 2 :(得分:0)
您无法将“不精确”双精度转换为“精确”十进制数,因为所需的“精度”根本就不存在(否则您为什么要转换?)。
如果你在Java中尝试类似的东西会发生这种情况:
BigDecimal x = new BigDecimal(0.1);
System.out.println(x);
该计划的输出是:
0.1000000000000000055511151231257827021181583404541015625
答案 3 :(得分:0)
嗯,你的精确度低于典型的双倍。你的有效数字很长,给你的范围从20亿到+20亿,超过9但精度不到10位。
这是一个未经测试的起点,您可以在PreciseNumbers上进行一些简单的数学运算
PreciseNumber Multiply(PreciseNumber lhs, PreciseNumber rhs)
{
PreciseNumber ret;
ret.s=lhs.s;
ret.e=lhs.e;
ret.s*=rhs.s;
ret.e+=lhs.e;
return ret;
}
PreciseNumber Add(PreciseNumber lhs, PreciseNumber rhs)
{
PreciseNumber ret;
ret.s=lhs.s;
ret.e=lhs.e;
ret.s+=(rhs.s*pow(10,rhs.e-lhs.e));
}
我没有处理任何重整化,但在这两种情况下都有一些地方你不得不担心流量过大/流失和精度损失。只是因为你自己做而不是让计算机把它当作双重处理,不会有同样的陷阱不存在。不失精度的唯一方法是跟踪所有数字。
答案 4 :(得分:0)
这是一个非常粗略的算法。我稍后会尝试填写一些细节。
取数字的log10得到指数。如果为正,则将双倍乘以10 ^ x,如果为负,则除以10 ^ -x。
以有效数为零开始。重复以下15次,因为double包含15位有效数字:
完成后,取剩余的double值并将其用于舍入:如果是>= 5
,则将其添加到有效数字。