用于存储在字符串中的小数的 MySQL Round() 函数

标签 mysql sql floating-point numbers

MySQL版本是5.66.22

当我对存储在 varchar 中的十进制数字使用 ROUND 函数时,我发现所有 .5 数字都有奇怪的行为

Select round(0.5)
1

Select round('0.5')
0

Select round('26.5' + 0.00)
26

但是

Select round(1.5)
2

Select round('1.5')
2

Select round(0.55, 1)
0.6

Select round('0.55', 1)
0.6

我检查了 Oracle DB (12c) 中的 ROUND 函数,它按预期工作

Select round('0.5') from dual 
1

Select round(0.5) from dual 
1

有人知道怎么解释吗?

所描述的 mysql round() 函数行为导致应用程序中出现“舍入”问题。为了解决我使用的问题:

Select round (CAST('0.5' AS DECIMAL(10,2)))
1

我知道在 varchar 中存储数字是糟糕的设计,但是这个应用程序是很久以前写的,现在没有人想重构代码

最佳答案

很有趣。该行为可以解释如下:

1) MySQL 在数字上下文 (ref) 中使用时将字符串转换为浮点值:

CREATE TABLE test AS (
    SELECT 0.5, '0.5' * 1 AS str_to_numeric
);

DESCRIBE test;

+----------------+--------------+------+-----+---------+-------+
| Field          | Type         | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+-------+
| 0.5            | decimal(2,1) | NO   |     | 0.0     |       |
| str_to_numeric | double       | NO   |     | 0       |       |
+----------------+--------------+------+-----+---------+-------+

2) 如手册中所述:

Rounding Behavior

The ROUND() function rounds differently depending on whether its argument is exact or approximate:

  • For exact-value numbers, ROUND() uses the “round half up” rule: A value with a fractional part of .5 or greater is rounded up to the next integer if positive or down to the next integer if negative. (In other words, it is rounded away from zero.) A value with a fractional part less than .5 is rounded down to the next integer if positive or up to the next integer if negative.

  • For approximate-value numbers, the result depends on the C library. On many systems, this means that ROUND() uses the “round to nearest even” rule: A value with any fractional part is rounded to the nearest even integer.

下面是一些说明 ROUND 函数最终行为的测试:

CREATE TABLE test(
   fix DECIMAL(10,2),
   arb DOUBLE
);

INSERT INTO test(fix, arb) VALUES
    (0.5, 0.5),
    (1.5, 1.5),
    (2.5, 2.5),
    (3.5, 3.5);

SELECT fix, ROUND(fix) fix_roundex, arb, ROUND(arb) arb_rounded
FROM test

+------+-------------+------+-------------+
| fix  | fix_roundex | arb  | arb_rounded |
+------+-------------+------+-------------+
| 0.50 |           1 |  0.5 |           0 |
| 1.50 |           2 |  1.5 |           2 |
| 2.50 |           3 |  2.5 |           2 |
| 3.50 |           4 |  3.5 |           4 |
+------+-------------+------+-------------+

您的解决方案(将数字字符串显式转换为 DECIMAL)是正确的。

关于用于存储在字符串中的小数的 MySQL Round() 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50063590/

相关文章:

sql - 不在 Hive QL 中

c - 如何引发浮点异常

c - 快速浮点abs函数

parsing - 将数字字符串解析为 float 有哪些风险?

java - 插入大量数据时如何修复 MySQL 数据截断错误?

sql - 根据是否存在另一个表行来选择行

mysql - 获取每个周期的条目数,不包括第一个(SQL 查询)

python - 打印 MySQLdb 运行的实际查询?

javascript - 使用 PHP 和 Javascript 删除选定 ID 的选定行

php - MySQL 所选行的大小