sqlserver设置整数三位一逗号_什么?浮点转整数还能不损失精度

浮点数转整数精度问题解决方案

b203465315273a859cc8d09d4559911a.png

前言

在软件开发中,尤其是金融行业的软件开发项目中,经常涉及到浮点数(用户界面显示价格)和整数(内部计算价格)之间进行数据转换,但是这数据在这两种类型间进行转换,又会导致浮点数的精度损失,比如下面的代码,为什么i_b输出内容不是29999900呢?

#include "stdafx.h"  #include "stdio.h"    int _tmain(int argc, _TCHAR* argv[])  {      double d_a = 2.0;      int i_a = d_a * 10000;      printf("d_a=%lf, i_a=%d ",d_a,i_a);        double d_b = 2999.99;      int i_b = d_b * 10000;      printf("d_b=%lf, i_b=%d ",d_b,i_b);        return 0;  }  

实际输出结果:

eaf50d503ea109dd62b89b82b0385acd.png

有什么方法解决精度损失问题呢?下面从C语言的角度跟大家分享几种设计思路,供大家参考。

应用场景举例

某券商炒股系统的客户端软件显示的股票行情价格都是小数,有些数据需要显示两位有效数字,有些需要显示三位有效数字。

09de29a1db4f7a4da30767973302b97c.png

某炒股软件行情界面

如上图所示,在行情界面,普通股票价格显示两位有效数字,指数价格显示三位有效数字。

fc4e624516051763b7b9683f19988463.png

某炒股软件持仓界面

如上图所示,在持仓界面,价格显示三位有效数字。

那么散户使用客户端下单炒股时,客户端把数据传入股票交易系统中进行股票交易或者行情计算时,难道也需要以小数类型进行数据运算吗?精度损失导致的金钱损失由谁来承担呢?数据类型的变化为什么会导致精度损失呢?有什么方案能够避免精度损失呢?

既然我们发现了这个问题,那咱们下面就去分析问题产生的原因,并尽量从根源上去解决问题。

浮点数在内存中的存储格式

C语言中,常用的整数类型是int,一般占用4个字节(不同的编译器,占据字节也不同);常用的浮点数类型为 float 和 double,float 占用 4 个字节,double 占用 8 个字节。因为整数类型在内存中的存储格式大家都清楚,所以就不再描述了,下面重点说一下浮点数在内存中的存储格式。

下图演示了 float 和 double 的存储格式,无论是单精度小数还是双精度小数,浮点数的内存被分成了如下三部分,当浮点数的类型确定后,每一部分的位数就是固定的。

(1)符号位 0代表正,1代表负

(2)指数位 用于存储科学计数法中的指数数据,并且采用移位存储

(3)尾数部分

2db7280902133f9a44eea96976a9f230.png

在内存中从高位到低位依次是符号位,指数位和尾数部分。float 的符号位占1位,指数位占8位,尾数部分占23位;double 的符号位占1位,指数位占11位,尾数部分占52位。

精度问题

对于十进制小数,整数部分转换成二进制使用"展除法"(就是不断除以 2,直到余数为 0),一个有限位数的整数一定能转换成有限位数的二进制。但是小数部分就不一定了,小数部分转换成二进制使用"乘二取整法"(就是不断乘以 2,直到小数部分为 0),一个有限位数的小数并不一定能转换成有限位数的二进制,只有末位是 5 的小数才有可能转换成有限位数的二进制,其它的小数都不行。

float 和 double 的尾数部分是有限的,固然不能容纳无限的二进制;即使小数能够转换成有限的二进制,也有可能会超出尾数部分的长度,此时也不能容纳。这样就必须"四舍五入",将多余的二进制"处理掉",只保留有效长度的二进制,这就涉及到了精度的问题。也就是说,浮点数不一定能保存真实的小数,很有可能保存的是一个近似值。

解决方案

方案一:

[数据格式]

客户端使用字符串或double类型存储价格数据(最多显示4位有效数字),但是数据传给交易系统时需要使用把double类型;

交易系统内部使用int64_t类型存储价格,在交易系统内部把价格扩大10000倍后进行计算;

备注:

1.因为客户端的价格精度有三位的情况,所以在交易系统内部把价格扩大10000倍后,把整数部分作为价格数值计算可以避免交易系统内部的精度损失。

2.之所以扩大10000倍,是为了方便客户端对小数点后第三位的精度进行计算保留的,比如有的客户端需要对小数点后第四位进行四舍五入,有的客户端可能需要使用五舍六入。

3.float浮点数默认的精度是6(即通过%f输出只显示6位小数),所以示例方案中设置成4位有效数字就可以了,足够使用了且精度还能够有所保证。

[转换方法]

客户端--->交易系统:

交易系统通过snprintf函数把double数据转换成字符串(使用%lf格式),去掉小数点后把四位小数(不足4位就在后面补0)和前面的整数部分拼接起来,然后通过stroll函数转换成整数存成int64_t类型。

交易系统--->客户端:

交易系统把int64_t类型数据直接除10000,后存成double类型。

[特点]

优点:

对客户端的代码开发没有影响。

缺点:

客户端收到的数据是四舍五入后的数值,客户端不能自己定义尾数的舍入方式;交易系统发给客户端的价格数据有可能会存在一点精度损失。

方案二:

[数据格式]

客户端使用字符串或double类型存储价格数据(最多显示4位有效数字),但是数据传给交易系统时需要使用把字符数组类型,比如交易系统和客户端约定使用一个char[19]数组存储价格数据,第1个元素作符号位,2~14存储整数(右对齐),15~18存储小数(左对齐),最后一个元素位置成'0'。

交易系统内部使用int64_t类型存储价格,在交易系统内部把价格扩大10000倍后进行计算;

[转换方法]

客户端--->交易系统:

交易系统把客户端传过来的字符数组通过strtoll函数转换成整数存成int64_t类型,然后再进行后续的计算处理。

交易系统--->客户端:

交易系统把int64_t类型数据通过snprintf函数,把整数存储在字符串中,并按照约定的char[19]数组格式进行数据整理后发给客户端。

[特点]

优点:

客户端和交易系统交互时没有精度损失,且客户端可以根据自己的需要选择使用四舍五入还是五舍六入。

缺点:

客户端代码开发会复杂一些,需要把交易系统发过来的字符串转成数值再进行显示。

总结:

本文只是通过C语言的字符数组作为中转,对整数和浮点数的转换提供了一种思路,在其他的一些编程语言中,一些string类或特殊接口也提供了类似的功能。但是不管是使用哪种语言,大家一定要先研究一下不同数据类型在内存中实际存放的方式,以及真正有效的精度范围,这样才可以保证转换出的数据是真实可信的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值