将 ruby on rails 中的更改列从 float 迁移到 double 更改更高值

Migrate changing column from float to double changing higher values in ruby on rails

提问人:Luana Maia 提问时间:1/19/2022 更新时间:1/23/2022 访问量:513

问:

我正在尝试进行迁移,将列类型从 float 更改为 double,因为 float 类型将数据库中的某些值四舍五入,例如:

过帐值:列类型为浮点数的数据库中23337840
值:23337800

过帐值:列类型为浮点型的数据库中23337870
值:3337900

当我运行迁移到双倍时,这些值被更正为正确的值,并且具有适当的精度,但是像 1 万亿这样的值是错误的。

浮点型列中的值:10000000000000
双精度型列中的值:999999995904

注意:如果在数据库中进行新的插入,并且该列为值为 1 万亿的 double 类型,则该值是正确的。仅当值从 float 迁移到 double 时,才会发生该错误。

有谁知道为什么会发生这种情况,因为双精度应该接受更大的值?有什么方法可以使数据库中已有的这些值保持正确?

MySQL 的Ruby-on-Rails Ruby 精度

评论

1赞 engineersmnky 1/19/2022
如果你需要精度(例如小数点右边),你真的应该使用 DECIMAL,如果你不需要,你真的应该使用 INTEGER(就像这篇文章中的所有数字一样)。DECIMAL 和 INTEGER 被称为“精确数值数据类型”,而 FLOAT 和 DOUBLE 都被称为“近似数值数据类型”(根据 MySQL 文档)。(我从未将后者用于任何应用程序)。至于你如何纠正(如果可以的话)你已经尝试/实现的东西,我会允许有更多DBA经验的人对此发表评论。
0赞 Stefan 1/20/2022
当将 1,000,000,000,000 存储为单个精度二进制浮点数时,它变为 999,999,995,904。如果将存储的 999,999,995,904 转换为双精度,则它保持 999,999,995,904 – 精度损失已经发生。
0赞 Luana Maia 1/22/2022
@engineersmnky 我还尝试使用精度为 64 和小数位 12 的 DECIMAL,它仍然对值进行了四舍五入。例如:浮点型列中的值:10000000000000 十进制(64,12)列中的值 类型:999999995904.0000000000000
0赞 Luana Maia 1/22/2022
@Stefan 当我将 1,000,000,000,000 存储为浮点数时,数据库中的值保持不变,没有更改或舍入。只有在列类型(float 到 double,或 float 到 decimal)的迁移中,数据库中的值才会发生更改。
1赞 Stefan 1/22/2022
@LuanaMaia 1,000,000,000,000 不能完全表示为单个精度(32 位)浮点数。最接近的 32 位浮点数略低:999,999,995,904。下一个更高的 32 位浮点数已经是 1,000,000,061,440。前者是数据库中存储的内容(如果您有 FLOAT(32 位)列。但是,如果您有 DOUBLE(64 位)列(或高精度的 DECIMAL),则可以精确存储该值。这就是为什么输入新值可以按预期工作,而旧值(来自以前的 FLOAT 列的值)会保留其精度损失的原因。

答:

1赞 Stefan 1/22/2022 #1

MySQL 数据库列存储(默认情况下)32 位单精度浮点数。这些值的精度有限,对于非常小或非常大的数字来说,这一点最为明显。例如,这些是最接近 1,000,000,000,000 的可用 32 位浮点值:(有各自的差异)FLOAT

 999999799296.0  # -200,704
 999999864832.0  # -135,168
 999999930368.0  #  -69,632
 999999995904.0  #   -4,096
1000000061440.0  #  +61,440
1000000126976.0  # +126,976
1000000192512.0  # +192,512

如果中间有一个值,它将转换为最接近的可用浮点数。尝试将整数 1,000,000,000,000 存储为 32 位浮点数实际上将存储 999,999,995,904.0,因为这是最接近的可用值,“仅”相差 4,096。

可以表示的值是可以表示的值的子集。这意味着将列从 转换为 是一种无损操作。没有进一步的精度损失,其数值不会改变。但是,无法从 32 位浮点数重建原始值,即将 999,999,995,904.0 从 FLOAT 转换为 DOUBLE 仍将是 999,999,995,904.0。FLOATDOUBLEFLOATDOUBLE

将新值存储到 DOUBLE 列中确实受益于更高的精度。这就是为什么您的列中可以有 1,000,000,000,000.0 的原因。DOUBLE

请注意,现在 Ruby 中的浮点数是 64 位的,即 Ruby 的浮点数对应于 MySQL 的浮点数。FloatDOUBLE