十进制值被 to_f 截断
Posted
技术标签:
【中文标题】十进制值被 to_f 截断【英文标题】:Decimal values are truncating with to_f 【发布时间】:2021-10-12 21:15:11 【问题描述】:我有一个名为 Item 的模型,我在其中更新 unit_price(数据类型为十进制)值,目前我在存储值时没有设置任何限制,按原样存储值。但是现在我可以看到这个 PG 错误 PG::NumericValueOutOfRange,当值超过限制时。
所以我只是想限制值并在控制台中检查一些东西,下面是数据。 (这里的数据我没有放所有的十进制值)
#<Item id: 167199, description: "192830139", category_id: 10327, unit_id: 5596, weight: 0.1e5, unit_price: 0.4083333333659917816764132553606237816656920077972709552126705653021442494641325536062378168e1
i = Item.find 167199
i.unit_price.to_f
=> 4.083333333659918
#<Item id: 167199, description: "192830139", category_id: 10327, unit_id: 5596, weight: 0.1e5, unit_price: 0.6511366980197836882065909262763993442019943880913510722934069011050182329156169820243980265070876781866034494363303661586489199452739290976143216266200531728395970406461889852558384421962422689303402903e-2
i.unit_price.to_f
=> 0.006511366980197837
我能知道to_f自动减少小数限制的原因是什么吗?解决此问题的最佳方法是什么,我只是在考虑一些有限制的截断。
【问题讨论】:
好吧,浮点数的精度有限。您可以改用BigDecimal。解决此问题的另一种常见方法是使用可能的最小单位将货币值存储为整数,例如1.95 美元,195
(美分)。
顺便说一句,你为什么会有这样的unit_price
?您是否在数据库之外使用浮点数? (你不应该)
不,我现在没有在 ruby 级别使用任何 to_f,目前存储我们从计算中得到的任何内容。
为什么需要大约 100 位小数?
有大量文章介绍了为什么永远不要使用浮点数进行货币单位或计算。这个问题对我来说听起来像是xy problem。
【参考方案1】:
你可以使用 ruby 内置的 .truncate() 方法
例如:
floatNum = 1.222222222222222
truncatedNum = floatNum.truncate(3) #3 is the number of decimal places you want
puts floatNum #returns 1.222
另一种方法是使用 .round() 方法
例如:
【讨论】:
对,没错,但是这个to_f会自动减少小数的限制是什么原因呢? 如果你注意到,数据类型是 bigdecimal。它用于存储比浮点数更大的小数。这与 java 中的 float 和 double 一样。这就是我的想法。这可能是错的。【参考方案2】:我能知道to_f自动减少小数限制的原因是什么吗?
原因是to_f
方法用于将对象转换为Float
s,即standard 64-bit double precision floating point numbers。这些数字的精度是有限的,因此必须在转换过程中自动降低原始对象的精度,以使其适合Float
。所有额外的精度都会丢失。
您似乎正在使用the BigDecimal
class。 The BigDecimal#to_f
method 将任意精度浮点十进制对象转换为Float
。自然,如果大小数比Float
s 允许的更精确,则在此转换过程中信息将丢失。如果超出限制,这种转换实际上可能会上溢或下溢。
我只是在想一些有限制的截断
There is a truncate
method 如果您想明确控制结果的精度。不会发生任何形式的舍入,there is a separate method for that。
BigDecimal#truncate
删除数字的整个小数部分,只留下一个整数。
BigDecimal('3.14159').truncate #=> 3
BigDecimal#truncate(n)
保留n
的精度,删除其余的。
BigDecimal('3.14159').truncate(3) #=> 3.141
【讨论】:
嗨@Matheus,谢谢你的建议,所以无论我们在截断中给出什么精度,这只会给出正确的值吗?那不会做任何舍入吧? @Developer 正确。截断只是删除指定精度之后的所有数字。不会发生任何类型的舍入。有一个单独的方法。以上是关于十进制值被 to_f 截断的主要内容,如果未能解决你的问题,请参考以下文章