涉及金钱存储或计算操作时,你们一般都使用什么数据类型 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
einsdisp
V2EX    程序员

涉及金钱存储或计算操作时,你们一般都使用什么数据类型

  •  
  •   einsdisp 2020-06-19 20:57:16 +08:00 11116 次点击
    这是一个创建于 1942 天前的主题,其中的信息可能已经有所发展或是发生改变。

    直接使用小数类型进行计算或存储(编程语言的 float 类型及数据库的 decimal 类型)

    还是乘以 100,以分为单位,使用整数类型进行计算及数据库存储?

    76 条回复    2020-06-30 15:54:44 +08:00
    wwti9
        1
    wwti9  
       2020-06-19 20:59:36 +08:00
    乘以 100
    yulitian888
        2
    yulitian888  
       2020-06-19 20:59:58 +08:00
    一万倍,长整型
    wujieyuan
        3
    wujieyuan  
       2020-06-19 21:02:50 +08:00
    我见过好多用 double 或者 String 的, 但是我觉得用分做单位,整数型最稳,除专业金融领域, 分单位足够用了
    cigarzh
        4
    cigarzh  
       2020-06-19 21:09:51 +08:00   1
    BigDecimal 一把梭,不要想自己搞什么花花肠子
    xuanbg
        5
    xuanbg  
       2020-06-19 21:17:26 +08:00
    金额使用 BigDecimal,金融行业 4 位小数,非金融 2 位小数。单价、利率什么的也用 BigDecimal,具体几位小数看需求。
    ackoly
        6
    ackoly  
       2020-06-19 21:18:54 +08:00 via iPhone
    用分一定要小心,见过本来要配置 100 元的,没加两个 00,变成 1 块。
    xuanbg
        7
    xuanbg 
       2020-06-19 21:19:19 +08:00
    double 、float 仅用于科学计算,用在业务系统里面就是搞笑。
    xuanbg
        8
    xuanbg  
       2020-06-19 21:21:36 +08:00
    用 string 就更搞笑了,完全是治标不治本。
    billlee
        9
    billlee  
       2020-06-19 21:52:50 +08:00
    不能一概而论,算加减法用定点数没有问题,但利息什么的要用乘法的时候,还是得用浮点数
    vanillaxxx
        10
    vanillaxxx  
       2020-06-19 22:00:17 +08:00 via iPhone
    bigdecimal
    pinkSlime
        11
    pinkSlime  
       2020-06-19 22:01:07 +08:00
    用 double 做清结算之类的 没被人砍死吗?
    还是想留后门自己薅羊毛
    Aruforce
        12
    Aruforce  
       2020-06-19 22:02:13 +08:00 via Android
    存储用分…计算的话最后用减……
    dswyzx
        13
    dswyzx  
       2020-06-19 22:37:57 +08:00
    数据库落库 decimal
    业务里各种千奇百怪的要求,四位小数,两位小数,不要小数.都要涉及取整操作.真烦
    3dwelcome
        14
    3dwelcome  
       2020-06-19 22:45:02 +08:00 via Android
    我要给 double 正名!按说精度也有 14 位,怎么就不能当存储类型了?
    统计有误差,是因为有计算中有误差会逐渐累计!你把误差去掉,或者用高精度大数类型替代一下就可以。
    光保存是没问题的。
    dodo2012
        15
    dodo2012  
       2020-06-19 22:48:59 +08:00
    decimal,,,
    F281M6Dh8DXpD1g2
        16
    F281M6Dh8DXpD1g2  
       2020-06-19 22:55:44 +08:00 via iPhone
    不用 decimal 早晚有问题
    zjsxwc
        17
    zjsxwc  
       2020-06-19 23:07:33 +08:00 via Android
    两位小数,decimal 逃
    Mac
        18
    Mac  
       2020-06-19 23:22:26 +08:00
    decimal
    rockyou12
        19
    rockyou12  
       2020-06-19 23:25:37 +08:00   4
    @3dwelcome 放屁,一看就是没踩过坑的,差一分财务都会对出来好不
    Sharuru
        20
    Sharuru  
       2020-06-19 23:42:33 +08:00 via Android
    BigDecimal 一把梭,多币种清算业务毫无压力。
    xingheng
        21
    xingheng  
       2020-06-19 23:44:29 +08:00
    万万没想到,居然还有这么多人写的系统用浮点类型,惊到了
    CEBBCAT
        22
    CEBBCAT  
       2020-06-20 00:00:28 +08:00
    @3dwelcome #14 学完了 IEEE 754 再来回帖吧……
    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 你在逗我????来电商三天包你下岗……
    zjsxwc
        24
    zjsxwc  
       2020-06-20 02:08:08 +08:00 via Android   1
    @xuanbg #8 原文:“用 string 就更搞笑了,完全是治标不治本。”
    回复:

    我倒觉得如果不用 decimal 而用 ` 两位小数的 string `或者 `乘以 100 的 integer `,貌似也都可以,因为好像也不会碰到无脑 double 浮点数运算的误差问题。
    xuanbg
        25
    xuanbg  
       2020-06-20 07:17:15 +08:00
    @zjsxwc string 看上去是没有精度问题,但你总要计算的吧,转 double 计算吗?所以我说「治标不治本」
    susunus
        26
    susunus  
       2020-06-20 07:59:26 +08:00 via iPhone
    老实说目前用的就是 Double 存储的,计算的时候,用用 BigDecimal 包裹,处理了下精度问题,那位老哥帮忙科普下,这样有啥风险
    Building
        27
    Building  
       2020-06-20 08:06:20 +08:00 via iPhone
    @xuanbg 人家都用 String 存储了你非要转 Double 做运算,不知道谁傻。
    reself
        28
    reself  
       2020-06-20 08:18:41 +08:00 via Android   1
    @susunus 浮点数只能表示数轴上的离散点,计算结果只能近似到最近的点,除非刚好能用 2 进制表示,例如 0.5,否则都是近似值。数轴上的点数量是无穷的,用有穷的浮点数来做计算,有偏差的概非常大。所以浮点数只能用在能容忍偏差的场景,比如图形绘制。除了数值偏差,浮点数还有众所周知的不能用==判定相等的特性。
    LukeEuler
        29
    LukeEuler  
       2020-06-20 08:46:47 +08:00
    小数字用 decimal,大数字用 string,bytes 之类的,自己设计运算方法。
    存储的和传输内容都是整数,只有显示时用 string 形式的浮点数。
    mitu9527
        30
    mitu9527  
       2020-06-20 08:58:10 +08:00
    参考 Money 模式,底层一般用 64 位的整型就可以了;如果长整型还不够用,那就考虑 GMP 吧。
    darrenfang
        31
    darrenfang  
       2020-06-20 09:16:56 +08:00
    @3dwelcome #14 以前写的代码用 double,突然发现数据库里面存的数据变成了 100.00000009,哈哈哈哈哈
    xuanbg
        32
    xuanbg  
       2020-06-20 09:43:54 +08:00
    @Building string 能做四则运算???
    des
        33
    des  
       2020-06-20 09:50:07 +08:00
    @xuanbg 可以
    rekulas
        34
    rekulas  
       2020-06-20 10:04:56 +08:00
    @xuanbg decimal 也是 string 按我了解的 多种语言的高精度计算貌似都用到了字符串做中间过程
    des
        35
    des  
       2020-06-20 10:07:06 +08:00
    并不需要转数值再计算,可以看一下这个 https://oi-wiki.org/math/bignum/
    yadiman
        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 越南盾元。

    通货膨胀,经常爆了。。。
    PopRain
        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 仅支持两位小数,而不是四位。)

    我想不出来什么应用可以超出它的范围
    PopRain
        38
    PopRain  
       2020-06-20 10:40:49 +08:00
    ,楼上的通货膨胀没有考虑到,以百万为货币单位的货币是很容易爆。。。
    mitu9527
        39
    mitu9527  
       2020-06-20 10:57:07 +08:00
    @PopRain 64 位整型,最大可以支持 900 万万亿,算上通货膨胀,也基本够用。真要是超出这个那就上 GMP ?
    U97F3
        40
    U97F3  
       2020-06-20 11:01:07 +08:00 via Android
    Numeric(19, 2)
    PopRain
        41
    PopRain  
       2020-06-20 12:30:42 +08:00
    @mitu9527 用整数也有问题,小数点的位置问题,常用货币 1 JPY=0.009357 USD,还有一些以亿元为单位的货币
    AV1
        42
    AV1  
       2020-06-20 13:15:06 +08:00
    @xuanbg
    当然可以。不仅四则,乘方、开方、对数、三角函数都可以。
    最简单的方法,模拟人类手工笔算一样操作每一位数就好了。只要资源足够,理论上没有精度上下限。
    Building
        43
    Building  
       2020-06-20 13:24:41 +08:00 via iPhone
    @xuanbg String 不能做四则运算......按你这个意思你是不是觉得计算机只要超过 UIntMax 的整数就没办法表示了?
    rapiz
        44
    rapiz  
       2020-06-20 14:00:05 +08:00   1
    一帖显示出 v2 上有多少人基本功不过关
    hantsy
        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 的处理,特别是国际化方面。
    mitu9527
        46
    mitu9527  
       2020-06-20 15:04:57 +08:00
    @PopRain 从数学角度看,不管你怎么处理总会存在误差,1/3 永远都除不尽,总是在某个位置要截断。我们要做的是把问题解决到可接受范围内,而不是完美解决。所以靠长整型和 Money 模式可以解决绝大多数问题,性能也还不错;如果业务真的要处理津巴布韦这种货币了,用 GMP,支持任意长度的整型,本质上是字符串,就是性能差。
    PopRain
        47
    PopRain  
       2020-06-20 15:15:21 +08:00
    @mitu9527 我是支持用 Decimal 的,基本在业务范围内的问题都可以解决,而整数很多需要自己处理,遇到某些不熟悉的程序员,可能出现大问题,譬如上面说的 1 元=100
    xuanbg
        48
    xuanbg  
       2020-06-20 15:50:35 +08:00
    @mitu9527
    @hantsy
    @Building 你们啊,用第三库或者自己造个轮子让 string 能够进行计算我会不懂? BigDecimal 本身也是一个轮子,内部的实现还是 int 和 long 。

    难道你们还能造个不用 int 、long 、double 、float 的轮子不成?
    yuzo555
        49
    yuzo555  
       2020-06-20 16:06:17 +08:00
    没研究,但是如果强行用 double 的话,我感觉,除了:

    1.误差好像基本上都是小于 10^-7 级别 (0.0000001) 的误差,每次计算完后都按照要求精度 round 一下应该可以解决这个问题;
    2.比较相等 == 的时候需要额外处理下。

    还有哪些需要注意的吗。
    mitu9527
        50
    mitu9527  
       2020-06-20 16:23:33 +08:00
    @xuanbg 原谅 PHP 太 low,没有 BigDecimal,只能用 GMP 或者 BC Math 扩展。然后最懒的方式就是用现成的、实现 Money 模式的库,还可以搞定币种、最小金额单位和分配等问题。
    everhythm
        51
    everhythm  
       2020-06-20 16:26:23 +08:00
    用 int,不同币种可以指定价格等级,类似 appstore

    不然等着被疯狂薅 1 分钱羊毛
    cubecube
        52
    cubecube  
       2020-06-20 16:30:10 +08:00
    @3dwelcome 其实我原来在银行的时候,非核心的外部系统,C 开发的,double 用来处理也没啥问题。基本上有效位数是够的。
    chenuu
        53
    chenuu  
       2020-06-20 17:53:25 +08:00 via Android
    Long,经过我们系统的大概千亿级
    zhuweiyou
        54
    zhuweiyou  
       2020-06-20 19:35:40 +08:00
    运算还是存储,存储的话,什么都行。涉及运算要么用大数类,要么单位分 整数运算。
    Vegetable
        55
    Vegetable  
       2020-06-20 19:40:08 +08:00
    好奇为什么 Decimal 不是标准答案
    celeron533
        56
    celeron533  
       2020-06-20 19:44:17 +08:00
    decimal
    sxd96
        57
    sxd96  
       2020-06-20 19:52:08 +08:00 via iPhone
    不知道楼上在喷什么,人家说用 double 存储,存储有问题吗?计算的时候拿出来怎么样精确计算各有各的方法,谁都知道 double 直接算会出问题。
    devinww
        58
    devinww  
       2020-06-20 19:59:11 +08:00
    BigDecimal
    xuanbg
        59
    xuanbg  
       2020-06-20 20:02:25 +08:00   1
    @sxd96 当然有问题,因为 double 的 1 不等于 int 的 1 啊
    sagaxu
        60
    sagaxu  
       2020-06-20 20:04:28 +08:00 via Android
    int string 或者 double 都行,只要处理得当。但是最后你会发现,你重新发明了 decimal 的轮子
    sagaxu
        61
    sagaxu  
       2020-06-20 20:10:12 +08:00 via Android
    @cubecube double 到万亿这个数量级精度就不够了,比如 5 万亿加 0.1 分
    joesonw
        62
    joesonw  
       2020-06-20 22:05:15 +08:00
    @sxd96 存储的就是不精确的啊. 你肉眼看到是 0.5, 实际数据并不是刚刚好 0.5 啊. 是 toString 的算法让你觉得存储的是精确的而已.
    sxd96
        63
    sxd96  
       2020-06-20 23:19:58 +08:00 via iPhone
    @xuanbg

    @joesonw

    double 能不能确保存储进去和读取出来的值是一样的?

    toString 将 double 解析成我肉眼看见的值,我在用 bigdecimal 计算的时候能不能确保转换后的值和 toString 的值是一样的?
    知道个 overflow underflow 就看哪都是问题了?用脑子再多想两遍。
    sxd96
        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 位小数。适用场景大大受限。

    事实上不做计算,仅做储存,能精确表示的范围也是相对固定的。但是我认为这个范围应该是够绝大多数公司撑到死了。
    sxd96
        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 溢出的,就很让人奇怪。
    realpg
        66
    realpg  
    PRO
       2020-06-21 10:22:11 +08:00
    必须用整形,至于乘 100 、乘 10000 、乘 1000000,看项目精度要求
    ysweics
        67
    ysweics  
       2020-06-21 12:43:36 +08:00
    金额还是统一 BigDecimal, 用别的搞法,出现问题,涉及到钱的一般都是大锅,望慎重
    LnTrx
        68
    LnTrx  
       2020-06-21 14:03:18 +08:00
    @sxd96
    4 位小数应该是 14bit 吧?
    只是存储问题当然有限,关键是实际计算的中间过程,容易出现防不胜防的错误判断、超出范围和舍入误差累积
    参见: http://www.lahey.com/float.htm
    ytmsdy
        69
    ytmsdy  
       2020-06-21 21:23:35 +08:00 via iPhone
    财务金额要是用 decimal 或者 float 的,一看就知道没有被四舍五入收拾过得!
    财务上差一分钱会计和出纳都会拉着你查账查到半夜,而最后的结果往往都是四舍五入导致的!
    所以!一定要用 int !一定要用 int !一定要用 int
    ytmsdy
        70
    ytmsdy  
       2020-06-21 21:25:13 +08:00 via iPhone
    @3dwelcome 兄弟别在这里误导人!到时候因为四舍五入的问题查账查到半夜的不是你,而是停了你建议用 double 的兄弟!
    datou
        71
    datou  
       2020-06-22 05:12:22 +08:00
    货币都是有定义最小单位的

    为何要用浮点数?
    luxinfl
        72
    luxinfl  
       2020-06-22 17:54:40 +08:00
    自打做系统,金额类的用的一直是 bigdecimal 。但是我们数据库保存的是元,总感觉别扭
    3dwelcome
        73
    3dwelcome  
       2020-06-23 16:19:39 +08:00
    @darrenfang 100.00000009 这种末尾本来就是计算误差,是随机数,你取出来的时候,直接 round 后断尾,精确度就完全没问题。
    double 有效数是 14 位,不需要用全,保证前 12 位没有数字误差,就可以了。
    IEEE 数据格式本来就是一个工具,在不同人手里,呈现的结果并不一样。代码是死的,人是活的,工具不趁手可以稍加改造。有坑多研究,总有绕过去的办法。
    码农的天职不是无脑调用 API,而是解决各种棘手问题。
    3dwelcome
        74
    3dwelcome  
       2020-06-23 16:29:13 +08:00
    @ytmsdy 四舍五入还不是因为计算精度误差。我说了 double 只是储存,如果计算累积精度不够,可以找高精度类来替代。
    你们对 double 内部格式都没有真正理解,在没有小数位的情况下,整数数值储存部分 bit,和 INT 在二进制上表示,是一模一样的。
    比如 double 存 1024 和 int 存 1024,内部就是一样的。只不过 double 里有指数位的存在,会对比特位进行 shift 操作。
    mysunshinedreams
        75
    mysunshinedreams  
       2020-06-28 17:33:02 +08:00
    其实 String 就行,平时真要计算的时候,使用 joda Money 类。
    CantSee
        76
    CantSee  
       2020-06-30 15:54:44 +08:00
    数据存储使用的金额单位是分,代码中金额操作使用 BigDecimal;
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2902 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 14:28 PVG 22:28 LAX 07:28 JFK 10:28
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86