首页 > 代码库 > 关于Currency类型和 TCurrencyFiled的悲剧

关于Currency类型和 TCurrencyFiled的悲剧

这2天程序出问题, 用户结算金额经常莫名其妙的多出了小数点后几位, 不用思考 肯定是因为浮点精度不准确的问题

查了一下, 程序中的数据类型使用的是Currency, 按照数据类型的描述, 这个金额类型应该是以实数形式存储的, 完全不会出现精度不准确的问题, 那为什么现实中还是会有莫名其妙的小数出现呢

继续跟踪程序, 发现数据在中转过程中使用了内存表控件, 而金额数据对应的字段类型正好是TCurrencyFiled

写点测试代码, 发现每次从内存表字段存取数据, 就会出现数值误差, 难道是这个字段有问题?

跟踪进源码, 发现TCurrencyFiled是从TDoubleField字段上继承下来的, 莫非这个字段使用的是Double格式数据存储??!!

 

看一下Help, 然后悲剧的发现, 人家早就告诉你了:

TCurrencyField encapsulates the fundamental behavior common to currency fields. TCurrencyField differs from its immediate ancestor TFloatField only in having a DataType of ftCurrency, and in formatting the value using a currency format (one that represents monetary values) by default. Currency fields can hold values in the range from (positive or negative) 5.0 * 10^-324 to 1.7 * 10^308 with an accuracy of 15 digits. Do not confuse TCurrencyField with the Currency data type. Currency fields use the double data type to store and manipulate their values. This data type is the format used by the physical database tables for currency fields. The TBCDField class uses the Currency data type to store and manipulate its value.If you use the Fields editor at design time to create a persistent field component for the currency field, you can access it by name at runtime. When using dynamic field components, you can access the TCurrencyField instance using the dataset?s Fields property or FieldByName method.

 

我嘞个擦.....Currency数据类型对应的居然不是TCurrencyField, 而应该是TBCDField......坑爹啊

 

最后补充一下: 一般我们常见的基本数据类型就不说了, 基本分为整型和浮点型, 其中整型是实数存储, 每个数据位都代表确切的数, 而浮点则是用指数存储, 表示成什么数完全取决于你要求的精度

所以...实数存储可以直接比较相等, 而指数一般无法比较(不是绝对不能比较)

这里最为特殊的, 就应该属于Currency类型了, 从数据表示上划分, 应该属于浮点类型数据, 因为他有小数, 而从存储形式上来看, 它又属于实数存储, 因为它的每个数据位都代表确切的值

如果还不明白, 可以使用下面的代码测试下:

var  i64: Int64;  c: Currency;begin  i64 := 0;  c := 123 / 7;  Move(c, i64, Sizeof(c));  ShowMessage(CurrToStr(c) + #13#10 + IntToStr(i64));end;

显示的结果是:

17.5714

175714

 

明白了吧, Currency实际上数据的存储和Int64是一样的, 只不过在使用的时候, 把最后4位认为是小数位而已