直接使用小数类型进行计算或存储(编程语言的 float 类型及数据库的 decimal 类型)
还是乘以 100,以分为单位,使用整数类型进行计算及数据库存储?
1 wwti9 2020-06-19 20:59:36 +08:00 乘以 100 |
![]() | 2 yulitian888 2020-06-19 20:59:58 +08:00 一万倍,长整型 |
![]() | 3 wujieyuan 2020-06-19 21:02:50 +08:00 我见过好多用 double 或者 String 的, 但是我觉得用分做单位,整数型最稳,除专业金融领域, 分单位足够用了 |
![]() | 4 cigarzh 2020-06-19 21:09:51 +08:00 ![]() BigDecimal 一把梭,不要想自己搞什么花花肠子 |
![]() | 5 xuanbg 2020-06-19 21:17:26 +08:00 金额使用 BigDecimal,金融行业 4 位小数,非金融 2 位小数。单价、利率什么的也用 BigDecimal,具体几位小数看需求。 |
6 ackoly 2020-06-19 21:18:54 +08:00 via iPhone 用分一定要小心,见过本来要配置 100 元的,没加两个 00,变成 1 块。 |
![]() | 7 xuanbg 2020-06-19 21:19:19 +08:00 double 、float 仅用于科学计算,用在业务系统里面就是搞笑。 |
![]() | 8 xuanbg 2020-06-19 21:21:36 +08:00 用 string 就更搞笑了,完全是治标不治本。 |
9 billlee 2020-06-19 21:52:50 +08:00 不能一概而论,算加减法用定点数没有问题,但利息什么的要用乘法的时候,还是得用浮点数 |
10 vanillaxxx 2020-06-19 22:00:17 +08:00 via iPhone bigdecimal |
![]() | 11 pinkSlime 2020-06-19 22:01:07 +08:00 用 double 做清结算之类的 没被人砍死吗? 还是想留后门自己薅羊毛 |
![]() | 12 Aruforce 2020-06-19 22:02:13 +08:00 via Android 存储用分…计算的话最后用减…… |
![]() | 13 dswyzx 2020-06-19 22:37:57 +08:00 数据库落库 decimal 业务里各种千奇百怪的要求,四位小数,两位小数,不要小数.都要涉及取整操作.真烦 |
![]() | 14 3dwelcome 2020-06-19 22:45:02 +08:00 via Android 我要给 double 正名!按说精度也有 14 位,怎么就不能当存储类型了? 统计有误差,是因为有计算中有误差会逐渐累计!你把误差去掉,或者用高精度大数类型替代一下就可以。 光保存是没问题的。 |
15 dodo2012 2020-06-19 22:48:59 +08:00 decimal,,, |
![]() | 16 F281M6Dh8DXpD1g2 2020-06-19 22:55:44 +08:00 via iPhone 不用 decimal 早晚有问题 |
![]() | 17 zjsxwc 2020-06-19 23:07:33 +08:00 via Android 两位小数,decimal 逃 |
![]() | 18 Mac 2020-06-19 23:22:26 +08:00 decimal |
20 Sharuru 2020-06-19 23:42:33 +08:00 via Android BigDecimal 一把梭,多币种清算业务毫无压力。 |
![]() | 21 xingheng 2020-06-19 23:44:29 +08:00 万万没想到,居然还有这么多人写的系统用浮点类型,惊到了 |
img src="https://cdn.v2ex.com/avatar/0638/c63e/230180_normal.png?m=1581961450" class="avatar" border="0" align="default" alt="lxml" data-uid="230180" /> | 23 lxml 2020-06-20 01:48:04 +08:00 @3dwelcome #14 你在逗我????来电商三天包你下岗…… |
![]() | 24 zjsxwc 2020-06-20 02:08:08 +08:00 via Android ![]() @xuanbg #8 原文:“用 string 就更搞笑了,完全是治标不治本。” 回复: 我倒觉得如果不用 decimal 而用 ` 两位小数的 string `或者 `乘以 100 的 integer `,貌似也都可以,因为好像也不会碰到无脑 double 浮点数运算的误差问题。 |
26 susunus 2020-06-20 07:59:26 +08:00 via iPhone 老实说目前用的就是 Double 存储的,计算的时候,用用 BigDecimal 包裹,处理了下精度问题,那位老哥帮忙科普下,这样有啥风险 |
28 reself 2020-06-20 08:18:41 +08:00 via Android ![]() @susunus 浮点数只能表示数轴上的离散点,计算结果只能近似到最近的点,除非刚好能用 2 进制表示,例如 0.5,否则都是近似值。数轴上的点数量是无穷的,用有穷的浮点数来做计算,有偏差的概非常大。所以浮点数只能用在能容忍偏差的场景,比如图形绘制。除了数值偏差,浮点数还有众所周知的不能用==判定相等的特性。 |
![]() | 29 LukeEuler 2020-06-20 08:46:47 +08:00 小数字用 decimal,大数字用 string,bytes 之类的,自己设计运算方法。 存储的和传输内容都是整数,只有显示时用 string 形式的浮点数。 |
30 mitu9527 2020-06-20 08:58:10 +08:00 参考 Money 模式,底层一般用 64 位的整型就可以了;如果长整型还不够用,那就考虑 GMP 吧。 |
31 darrenfang 2020-06-20 09:16:56 +08:00 @3dwelcome #14 以前写的代码用 double,突然发现数据库里面存的数据变成了 100.00000009,哈哈哈哈哈 |
![]() | 35 des 2020-06-20 10:07:06 +08:00 并不需要转数值再计算,可以看一下这个 https://oi-wiki.org/math/bignum/ |
![]() | 36 yadiman 2020-06-20 10:13:41 +08:00 某金融,13 位整形,minor unit,配合 decimal flag,然后转化成 major unit 。 比如,美金,decimal flag 就是 2,所以,100 minior unit = 1 USD major unit 越南盾,decima flag 就是 0,10000 minor unit 就是 10000 越南盾元。 通货膨胀,经常爆了。。。 |
![]() | 37 PopRain 2020-06-20 10:38:31 +08:00 c# Decimal 值类型表示介于正 79,228,162,514,264,337,593,543,950,335 和负 79,228,162,514,264,337,593,543,950,335 之间的十进制数字。 sql server money -922,337,203,685,477.5808 到 922,337,203,685,477.5807 (对于 Informatica,为 -922,337,203,685,477.58 到 922,337,203,685,477.58 。Informatica 仅支持两位小数,而不是四位。) 我想不出来什么应用可以超出它的范围 |
![]() | 38 PopRain 2020-06-20 10:40:49 +08:00 ,楼上的通货膨胀没有考虑到,以百万为货币单位的货币是很容易爆。。。 |
![]() | 40 U97F3 2020-06-20 11:01:07 +08:00 via Android Numeric(19, 2) |
![]() | 41 PopRain 2020-06-20 12:30:42 +08:00 @mitu9527 用整数也有问题,小数点的位置问题,常用货币 1 JPY=0.009357 USD,还有一些以亿元为单位的货币 |
![]() | 42 AV1 2020-06-20 13:15:06 +08:00 |
![]() | 43 Building 2020-06-20 13:24:41 +08:00 via iPhone @xuanbg String 不能做四则运算......按你这个意思你是不是觉得计算机只要超过 UIntMax 的整数就没办法表示了? |
44 rapiz 2020-06-20 14:00:05 +08:00 ![]() 一帖显示出 v2 上有多少人基本功不过关 |
45 hantsy 2020-06-20 14:13:22 +08:00 用 Java 的话,有 Java Money 标准(JSR354),和 Java DateTime 一个时期的产品,可惜一直没进 JDK 。 但是这个项目已经很成熟,https://javamoney.github.io/ ,官方提供了 JPA 扩展,也就是说直接用关系数据库没问题的。 @cigarzh @xuanbg 遇到金融数据,自然想到 BigDecimal 当然是最基本的常识。但是 Money 还包括了相应 Currency 的处理,特别是国际化方面。 |
46 mitu9527 2020-06-20 15:04:57 +08:00 @PopRain 从数学角度看,不管你怎么处理总会存在误差,1/3 永远都除不尽,总是在某个位置要截断。我们要做的是把问题解决到可接受范围内,而不是完美解决。所以靠长整型和 Money 模式可以解决绝大多数问题,性能也还不错;如果业务真的要处理津巴布韦这种货币了,用 GMP,支持任意长度的整型,本质上是字符串,就是性能差。 |
![]() | 47 PopRain 2020-06-20 15:15:21 +08:00 @mitu9527 我是支持用 Decimal 的,基本在业务范围内的问题都可以解决,而整数很多需要自己处理,遇到某些不熟悉的程序员,可能出现大问题,譬如上面说的 1 元=100 |
![]() | 48 xuanbg 2020-06-20 15:50:35 +08:00 |
![]() | 49 yuzo555 2020-06-20 16:06:17 +08:00 没研究,但是如果强行用 double 的话,我感觉,除了: 1.误差好像基本上都是小于 10^-7 级别 (0.0000001) 的误差,每次计算完后都按照要求精度 round 一下应该可以解决这个问题; 2.比较相等 == 的时候需要额外处理下。 还有哪些需要注意的吗。 |
50 mitu9527 2020-06-20 16:23:33 +08:00 @xuanbg 原谅 PHP 太 low,没有 BigDecimal,只能用 GMP 或者 BC Math 扩展。然后最懒的方式就是用现成的、实现 Money 模式的库,还可以搞定币种、最小金额单位和分配等问题。 |
![]() | 51 everhythm 2020-06-20 16:26:23 +08:00 用 int,不同币种可以指定价格等级,类似 appstore 不然等着被疯狂薅 1 分钱羊毛 |
![]() | 52 cubecube 2020-06-20 16:30:10 +08:00 @3dwelcome 其实我原来在银行的时候,非核心的外部系统,C 开发的,double 用来处理也没啥问题。基本上有效位数是够的。 |
![]() | 53 chenuu 2020-06-20 17:53:25 +08:00 via Android Long,经过我们系统的大概千亿级 |
54 zhuweiyou 2020-06-20 19:35:40 +08:00 运算还是存储,存储的话,什么都行。涉及运算要么用大数类,要么单位分 整数运算。 |
![]() | 55 Vegetable 2020-06-20 19:40:08 +08:00 好奇为什么 Decimal 不是标准答案 |
56 celeron533 2020-06-20 19:44:17 +08:00 decimal |
![]() | 57 sxd96 2020-06-20 19:52:08 +08:00 via iPhone 不知道楼上在喷什么,人家说用 double 存储,存储有问题吗?计算的时候拿出来怎么样精确计算各有各的方法,谁都知道 double 直接算会出问题。 |
![]() | 58 devinww 2020-06-20 19:59:11 +08:00 BigDecimal |
![]() | 60 sagaxu 2020-06-20 20:04:28 +08:00 via Android int string 或者 double 都行,只要处理得当。但是最后你会发现,你重新发明了 decimal 的轮子 |
62 joesonw 2020-06-20 22:05:15 +08:00 @sxd96 存储的就是不精确的啊. 你肉眼看到是 0.5, 实际数据并不是刚刚好 0.5 啊. 是 toString 的算法让你觉得存储的是精确的而已. |
![]() | 63 sxd96 2020-06-20 23:19:58 +08:00 via iPhone |
![]() | 64 sxd96 2020-06-20 23:47:46 +08:00 @sxd96 自己更正一点。 double 的 frac 总共 52bit,精确表示数值加上前面的 sign 1bit 总共能精确表示正负 2^52 。 假设再加上可能有的 4 位小数,能精确表示的金额只有正负 2^48=281,474,976,710,656 以及后面的 4 位小数。适用场景大大受限。 事实上不做计算,仅做储存,能精确表示的范围也是相对固定的。但是我认为这个范围应该是够绝大多数公司撑到死了。 |
![]() | 65 sxd96 2020-06-21 00:09:30 +08:00 @sxd96 脑子瓦特了,再更正一点。 如果要准确表示上面假设里说的 4 位小数,要 10bit 不是 4bit 。那么金额的整数部分就只有 2^42 = 4,398,046,511,104 。 在精确表示的范围内直接用 double 做算术运算也不会有问题的,也用不到什么 bigdecimal 。但是这个范围确实比我想当然的小了不少。4 个小数点存在的情况下,只能做到 4 万亿内的精确。 但是上面说的所谓 0.5 存着不是 0.5 就是开玩笑,double 的 1 那就是 1 。 用 decimal 肯定是第一反应也是最好的选择,当然没意见。但是看着这么多瞎说 double 溢出的,就很让人奇怪。 |
![]() | 66 realpg PRO 必须用整形,至于乘 100 、乘 10000 、乘 1000000,看项目精度要求 |
![]() | 67 ysweics 2020-06-21 12:43:36 +08:00 金额还是统一 BigDecimal, 用别的搞法,出现问题,涉及到钱的一般都是大锅,望慎重 |
![]() | 68 LnTrx 2020-06-21 14:03:18 +08:00 @sxd96 4 位小数应该是 14bit 吧? 只是存储问题当然有限,关键是实际计算的中间过程,容易出现防不胜防的错误判断、超出范围和舍入误差累积 参见: http://www.lahey.com/float.htm |
![]() | 69 ytmsdy 2020-06-21 21:23:35 +08:00 via iPhone 财务金额要是用 decimal 或者 float 的,一看就知道没有被四舍五入收拾过得! 财务上差一分钱会计和出纳都会拉着你查账查到半夜,而最后的结果往往都是四舍五入导致的! 所以!一定要用 int !一定要用 int !一定要用 int |
![]() | 70 ytmsdy 2020-06-21 21:25:13 +08:00 via iPhone @3dwelcome 兄弟别在这里误导人!到时候因为四舍五入的问题查账查到半夜的不是你,而是停了你建议用 double 的兄弟! |
![]() | 71 datou 2020-06-22 05:12:22 +08:00 货币都是有定义最小单位的 为何要用浮点数? |
![]() | 72 luxinfl 2020-06-22 17:54:40 +08:00 自打做系统,金额类的用的一直是 bigdecimal 。但是我们数据库保存的是元,总感觉别扭 |
![]() | 73 3dwelcome 2020-06-23 16:19:39 +08:00 @darrenfang 100.00000009 这种末尾本来就是计算误差,是随机数,你取出来的时候,直接 round 后断尾,精确度就完全没问题。 double 有效数是 14 位,不需要用全,保证前 12 位没有数字误差,就可以了。 IEEE 数据格式本来就是一个工具,在不同人手里,呈现的结果并不一样。代码是死的,人是活的,工具不趁手可以稍加改造。有坑多研究,总有绕过去的办法。 码农的天职不是无脑调用 API,而是解决各种棘手问题。 |
![]() | 74 3dwelcome 2020-06-23 16:29:13 +08:00 @ytmsdy 四舍五入还不是因为计算精度误差。我说了 double 只是储存,如果计算累积精度不够,可以找高精度类来替代。 你们对 double 内部格式都没有真正理解,在没有小数位的情况下,整数数值储存部分 bit,和 INT 在二进制上表示,是一模一样的。 比如 double 存 1024 和 int 存 1024,内部就是一样的。只不过 double 里有指数位的存在,会对比特位进行 shift 操作。 |
75 mysunshinedreams 2020-06-28 17:33:02 +08:00 其实 String 就行,平时真要计算的时候,使用 joda Money 类。 |
![]() | 76 CantSee 2020-06-30 15:54:44 +08:00 数据存储使用的金额单位是分,代码中金额操作使用 BigDecimal; |