何时使用小数或双精度数

3
快速提醒:我将使用“Float”一词来指代.Net float和仅具有7个有效数字的SQL float。我将使用“Double”一词来指代.Net double和具有15个有效数字的SQL float。我也意识到这与其他关于小数/双精度浮点数的帖子非常相似,但是这些帖子中的答案非常不一致,我真的想要一些针对我的特定情况的建议...
我是一个正在重写旧应用程序的团队的一部分。原始应用程序使用了浮点数(7位数字)。当然,这会导致问题,因为应用程序进行了大量计算,舍入误差很快就累积了。在某个时候,许多这些浮点数被更改为小数。后来,数据库中的浮点数(7)全部变成了双精度浮点数(15)。之后我们还有几个涉及双精度浮点数的计算错误,它们也被更改为小数。
今天,数据库中所有浮点数的三分之一左右是小数,其余的是双精度浮点数。我的团队想要“标准化”数据库中的所有浮点数(以及新的.Net代码),以仅使用小数或双精度浮点数,除非必须使用另一种类型。大多数团队都决定使用小数;我是唯一一个支持使用双精度浮点数而不是小数的人。这是原因...
1.数据库中的大多数数字仍然是双精度浮点数(尽管应用程序代码的大部分仍然使用浮点数),如果要将所有浮点数/双精度浮点数更改为小数,需要付出更多的努力。 2.对于我们的应用程序,存储的字段都不是“精确”的十进制量。它们中没有一个是货币数量,并且大多数代表某种“自然”测量(例如质量、长度、体积等),因此双精度浮点数的16个有效数字已经比我们最初的测量值更精确得多。 3.许多表中都有存储在两列中的测量值:一个用于值;一个用于计量单位。这可能会导致单个列中的值之间存在巨大的比例差异。例如,一个列可以以pCi/g或Ci/m3(1 Ci = 1000000000000 pCi)的形式存储值。由于单个小数列中的所有值必须具有相同的比例(即...小数点前后分配的数字数量),我担心我们会遇到溢出和舍入问题。
我的队友们认为:
1.由于双精度浮点数无法准确地表示1/10,因此它们不如小数准确精确,而且它们具有16个有效数字。 2.即使我们没有追踪货币,该应用程序也是一个库存系统,用于跟踪材料(主要是克量),并且需要“尽可能准确”。 3.即使将浮点数更改为双精度浮点数后,我们仍然继续从使用双精度浮点数的计算中得到错误结果。将这些列(以及应用程序代码)更改为小数会导致这些计算产生预期的结果。
我坚信出现原始问题的原因是由于浮点数只有7个有效数字,简单算术(例如10001 * 10001)导致数据迅速使用了它们所拥有的少数有效数字。我认为这与二进制浮点数如何仅能“近似”十进制值无关,并且使用双精度浮点数可以解决此问题。
我认为使用双精度浮点数的问题是它们与小数一起用于计算时,值将在数据类型之间反复转换。许多计算会在计算的中间步骤中进行四舍五入!
我试图说服我的团队不要把所有东西都变成小数。数据库中大多数值都没有超过5或6个有效数字。不幸的是,我在团队中的地位比其他成员低,他们对此看法截然不同。
因此,我的问题是...
1. 我是否在担心无谓的事情?在一个具有近200个数据库表、数百个交易和5到6年的重写计划的应用程序中,几乎完全使用小数而不使用双精度浮点数会造成任何实际的危害吗? 2. 使用小数实际上是否解决了双精度浮点数无法解决的问题?根据我的研究,小数和双精度浮点数都容易出现涉及任意分数(例如添加1/3)的舍入误差,唯一解决此问题的方法是在比较双精度浮点数和/或小数时将任何值视为“相等”,只要在一定公差范围内即可。 3. 如果使用双精度浮点数更合适,除了我已经提出的意见之外,还有什么论据可以说服我的团队不将“所有东西”都更改为小数?

对数字进行了什么样的计算?如果它们仅限于严格的有理运算,那么您应该考虑将数字存储为整数对(分子和分母),这样就不会出现任何舍入误差。 - Dai
一个典型的计算可能涉及到某种材料的放射性。用户希望看到结果的物质量和单位(pCi/g、Ci/m3或MBq/l)。如果结果需要用体积来表示,那么我们还必须包括涉及有多少克以及容器的大小的计算。 - NuclearProgrammer
在我看来,这些看起来像是普通的算术运算 - 只要您不使用任何单向函数或无理值(如 Sqrt),您应该可以将其移动到内部有理表示系统。 - Dai
@NuclearProgrammer,你的问题表述得非常清楚。恰好我也有同样的疑问,你能否分享一下你的发现,即你最终达成了什么共识。 - Sunny
2个回答

1

当您需要完美精度作为十进制数字(财务数据,成绩)时,请使用decimal

当您存储天然不精确的数据(测量,温度),希望进行更快速的数学运算并可以牺牲少量不精确性时,请使用doublefloat。由于您似乎只存储各种测量值(本来就有一定的精度),因此float是合适的选择(如果需要超过7位数的精度,则使用double)。

使用小数是否真正解决了不能使用双倍数解决的问题?

实际上没有 - 数据的准确性仅取决于用于生成数据的测量。您能否确切地说一个测量数量为123.4567克?用于测量它的设备是否具有那个级别的精度?

要处理“舍入误差”,我认为您无法确定1234.5克的测量值是否恰好是一半 - 它同样可能是1234.49克,这将被四舍五入。

你需要决定的是“可接受的精度水平”,并始终将其舍入到该精度水平作为最后一步。不要舍入数据或中间计算。
如果使用double更合适,除了已经提出的理由,我还能提出什么论点来说服我的团队不改变所有内容为十进制?
除了切换所花费的时间之外,你真正牺牲的只是速度。唯一知道“多少速度”是尝试两种方式并测量差异。

0

你最好尽力不要失去精度。我猜我的错误可能会让你选择双精度。

===> 我做了一些错误的算术,结果返回了一些非常奇怪的东西: 给定 0.60,它返回 5

int get_index(double value) {
    if (value < 0 || value > 1.00)
        return -1;
    return value / 0.10;
}

我已经修复了它:

int get_index(double value) {
    if (value < 0 || value > 1.00)
        return -1;
    return (value * 100000000) / (0.10 * 100000000);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接