对于货币应用程序来说,使用整数作为小数系数而不是浮点数是个好主意吗?
来源:stackoverflow
时间:2024-04-01 22:21:36 214浏览 收藏
Golang小白一枚,正在不断学习积累知识,现将学习到的知识记录一下,也是将我的所得分享给大家!而今天这篇文章《对于货币应用程序来说,使用整数作为小数系数而不是浮点数是个好主意吗?》带大家来了解一下##content_title##,希望对大家的知识积累有所帮助,从而弥补自己的不足,助力实战开发!
我的应用程序需要小数数量乘以货币价值。
例如,$65.50 × 0.55 小时 = $36.025
(四舍五入为 $36.03
)。
我知道浮点数不应该用来表示金钱,因此我将所有货币值存储为美分。 $65.50
在上式中存储为 6550
(整数)。
对于小数系数,我的问题是 0.55
没有 32 位浮点表示。在上面的用例中,0.55 小时 == 33 分钟
,因此 0.55
是我的应用程序需要准确考虑的特定值的示例。 0.550000012
的浮点表示是不够的,因为用户不会理解附加的 0.000000012
来自哪里。我不能简单地在 0.550000012
上调用舍入函数,因为它会舍入到整数。
乘法解决方案
为了解决这个问题,我的第一个想法是将所有数量存储为整数并乘以×1000。因此用户输入的0.55
在存储时将变成550
(整数)。所有计算都将在没有浮点数的情况下进行,然后在向用户呈现结果时简单地除以 1000(整数除法,而不是浮点数)。
我意识到这将永久限制我的小数点后 3 位 精确。如果我认为 3 足以满足我的一生 应用程序,这种方法有意义吗?
如果我使用整数除法,是否存在潜在的舍入问题?
这个过程有名字吗? 编辑:正如@SergGr所示,这是定点算术。
有更好的方法吗?
编辑:
我应该澄清一下,这不是特定时间的。它适用于一般数量,例如 1.256 磅面粉
、1 sofa
或 0.25 小时
(想想发票)。
我在这里尝试复制的是 Postgres 的 extra_float_digits = 0
功能的更精确版本,如果用户输入 0.55
(float32),数据库将存储 0.550000012
但在查询结果时返回 0。 55
这似乎正是用户输入的内容。
我愿意将此应用程序的精度限制为小数点后 3 位(这是商业问题,而不是科学问题),因此这就是让我考虑使用 × 的原因。 1000
方法。
我正在使用 Go 编程语言,但我对通用跨语言解决方案感兴趣。
解决方案
注意:这是按照 Matt 的要求将不同的评论合并为一个连贯的答案的尝试。
TL;DR
- 是的,这种方法很有意义,但很可能不是最佳选择
- 是的,存在舍入问题,但无论您使用哪种表示形式,都不可避免地会出现一些问题
- 您建议使用的名称为 Decimal fixed point numbers
- 我认为是的,有一个更好的方法,那就是为您的语言使用一些标准或流行的 decimal floating point numbers 库(Go 不是我的母语,所以我不能推荐一个)
在 PostgreSQL 中,最好使用 numeric
(例如 Numeric(15,3)
之类的东西),而不是 float4
/float8
和 extra_float_digits
的组合。实际上,这就是 PostgreSQL doc on Floating-Point Types 中的第一项所建议的内容:
- 如果您需要精确的存储和计算(例如货币金额),请改用
numeric
类型。
有关如何存储非整数的更多详细信息
首先有一个基本事实,即 [0;1]
范围内有无限多个数字,因此显然无法将每个数字存储在任何有限的数据结构中。这意味着您必须做出一些妥协:无论您选择哪种方式,都会有一些数字您无法准确存储,因此您必须进行舍入。
另一个重要的一点是,人们习惯于基于 10 的系统,在该系统中,只有 2^a*5^b
形式的数字除法结果可以用有限位数表示。对于每个其他有理数,即使您以某种方式将其存储为精确的形式,您也必须在格式化阶段进行一些截断和舍入以供人类使用。
存储数字的方法可能有无数种。在实践中,只有少数被广泛使用:
floating point numbers 具有两个主要的二进制分支(这是当今大多数硬件本身实现的内容,也是大多数语言支持的内容,如
float
或double
)和 decimal。这是存储尾数和指数的格式(可以是负数),所以数字是mantissa * base^exponent
(我省略了符号,只是说它在逻辑上是尾数的一部分,尽管实际上它通常是单独存储的)。二进制与十进制由base
指定。例如,0.5
将以二进制形式存储为一对(1,-1)
,即1*2^-1
,以十进制形式存储为一对(5,-1)
,即5*10^-1
。理论上,您也可以使用任何其他基数,但实际上只有 2 和 10 作为基数才有意义。fixed point numbers,二进制和十进制的除法相同。这个想法与浮点数相同,但所有数字都使用一些固定指数。您建议的实际上是一个十进制定点数,指数固定为
-3
。我见过在一些没有内置浮点数支持的嵌入式硬件上使用二进制定点数,因为可以使用整数算术以合理的效率实现二进制定点数。至于十进制定点数,实际上实现起来并不比十进制浮点数容易多少,但灵活性却低得多。有理数格式,即该值存储为一对
(p, q)
,表示p/q
(通常是q>0
,因此符号存储在p
中,并且p=0、q=1
表示 zqbc) zqb0或gcd(p,q) = 1
对于每个其他数字)。通常这首先需要一些大整数算术才有用(这里是 math.big.Rat 的 Go 示例)。实际上,这对于某些问题可能是一种有用的格式,人们经常忘记这种可能性,可能是因为它通常不是标准库的一部分。另一个明显的缺点是,正如我所说,人们不习惯用有理数来思考(你能轻松比较123/456
或213/789
哪个更大吗?),所以你必须将最终结果转换为其他形式。另一个缺点是,如果您有很长的计算链,内部数字(p
和q
)可能很容易变成非常大的值,因此计算速度会很慢。存储计算的中间结果仍然可能有用。
实际上,也分为任意长度和固定长度表示。例如:
Go
math.big.Float
是任意长度的浮点二进制表示.Net
decimal
是固定长度浮点十进制表示Java
BigDecimal
是任意长度的浮点十进制表示
实际上,我想说,解决您的问题的最佳解决方案是一些足够大的固定长度浮点十进制表示(例如.Net decimal
)。任意长度的实现也可以工作。如果您必须从头开始实现,那么您对固定长度定点十进制表示的想法可能没问题,因为它是您自己实现的最简单的事情(比以前的替代方案容易一点),但它可能会成为某些方面的负担存储结果的另一种解决方案是使用值的有理形式。您可以用两个等于 p/q
的整数值来解释该数字,这样 p
和 q
都是整数。因此,您可以对数字进行更精确的计算,并使用两个整数格式的有理数进行一些数学运算。
今天关于《对于货币应用程序来说,使用整数作为小数系数而不是浮点数是个好主意吗?》的内容就介绍到这里了,是不是学起来一目了然!想要了解更多关于的内容请关注golang学习网公众号!
-
502 收藏
-
502 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
139 收藏
-
204 收藏
-
325 收藏
-
477 收藏
-
486 收藏
-
439 收藏
-
357 收藏
-
352 收藏
-
101 收藏
-
440 收藏
-
212 收藏
-
143 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习