【问题标题】:Mysql - round half down or half upMysql - 半向下或半向上
【发布时间】:2015-11-19 13:01:36
【问题描述】:

在使用 MySql 的 sql 查询中,有没有办法指定是否要将字段值 4.485 舍入为 4.48(下半)或 4.49(上半)?

谢谢

【问题讨论】:

  • 鉴于目标小数位数,4.485 始终舍入为 4.48。总是。这就是四舍五入的工作原理。 4.485 到 4.48 是truncating
  • 也许在 mysql 中总是向下取整,但在 PHP 中可以定义

标签: mysql rounding


【解决方案1】:

即使这个问题是多年前的问题,我也遇到了同样的问题,并找到了我想分享的不同解决方案。

就是这样:当最后一个小数为 5 时,您可以使用条件函数明确决定要舍入的方式。 例如,如果你想用 2 位小数四舍五入:

SELECT IF ((TRUNCATE(mynumber*1000,0) mod 5) = 0,CEIL(mynumber*100)/100,ROUND(mynumber,2)) as value FROM mytable

如果要向下舍入一半,可以使用 FLOOR 函数而不是 CEIL;如果你想要不同的小数位数,比如 n,你可以改变 TRUNCATE 调用中的乘数 (10^n+1) 并将它传递给 ROUND 函数。

SELECT IF ((TRUNCATE(mynumber*([10^*n+1*]),0) mod 5) = 0,[CEIL|FLOOR](mynumber*100)/100,ROUND(mynumber,[*n*])) as value FROM mytable

【讨论】:

  • 太棒了!正是我所追求的。谢谢分享!!
【解决方案2】:

基于官方的 MySQL documentation 没有办法指定舍入策略。默认情况下,ROUND 对精确值数字使用“四舍五入”规则。 但是,我需要一个行为类似于java.math.BigDecimaljava.math.RoundingMode.HALF_DOWN 模式的函数。所以我找到的唯一解决方案是创建自己的函数。

代码(在 MySQL 5.7 中测试)

DELIMITER //
DROP FUNCTION IF EXISTS roundHalfDown //
CREATE FUNCTION roundHalfDown (
    numberToRound DECIMAL(25,15),
    roundingPrecision TINYINT(2)
)
    RETURNS DECIMAL(25,15)
BEGIN
    DECLARE digitPosition TINYINT (2) UNSIGNED DEFAULT 0;
    DECLARE digitToRound TINYINT (2) DEFAULT -1;
    DECLARE roundedNumber DECIMAL(20,6) DEFAULT 0;

    SET digitPosition = INSTR(numberToRound, '.');

    IF (roundingPrecision < 0) THEN
        SET digitPosition = digitPosition + roundingPrecision;
    ELSE
        SET digitPosition = digitPosition + roundingPrecision + 1;
    END IF;

    IF (digitPosition > 0
        AND digitPosition <= CHAR_LENGTH(numberToRound)
    ) THEN
        
        SET digitToRound = CAST(
                SUBSTR(
                numberToRound, 
                digitPosition, 
                1
            ) AS UNSIGNED
        );

        SET digitPosition = digitPosition - 1;
    END IF;

    IF (digitToRound > -1) THEN

        IF (digitToRound > 5) THEN

            SET roundedNumber = ROUND(numberToRound, roundingPrecision);
        ELSEIF (digitToRound = 5 AND CAST(
                SUBSTR(
                REPLACE(numberToRound, '.', ''), 
                digitPosition + 1
            ) AS UNSIGNED) > 0) THEN
            SET roundedNumber = ROUND(numberToRound, roundingPrecision);
        ELSE
            SET roundedNumber = TRUNCATE(numberToRound, roundingPrecision);
        END IF;

    ELSEIF (roundingPrecision > 0) THEN
        SET roundedNumber = numberToRound;
    END IF;

    RETURN roundedNumber;
END //
DELIMITER ;

测试用例

SELECT roundHalfDown(1.541, 2); #1.54
SELECT roundHalfDown(1.545, 2); #1.54
SELECT roundHalfDown(1.5451, 2); #1.55
SELECT roundHalfDown(1.54500001, 2); #1.55
SELECT roundHalfDown(1.54499999, 2); #1.54
SELECT roundHalfDown(-1.545, 2); #-1.54
SELECT roundHalfDown(-1.5451, 2); #-1.55
SELECT roundHalfDown(555, 0); #555
SELECT roundHalfDown(1000999, -1); #1001000
SELECT roundHalfDown(1000999, -2); #1001000
SELECT roundHalfDown(1000999, -3); #1001000
SELECT roundHalfDown(1000999, -4); #1000000

我在另一个 Stack Overflow 问题中使用了 this answer,但我针对我的具体情况做了一些修改。

【讨论】:

    【解决方案3】:

    这篇文章的公认答案提供了一个很好的解决方案: MySQL - How can I always round up decimals?

    即乘以 10^n,其中 n 是您要四舍五入的小数位,然后调用 ceil 向上舍入或 floor 向下舍入并除以 10^n。

    【讨论】:

      【解决方案4】:

      您可以使用 CEIL(或 CEILING)和 FLOOR 函数向上/向下舍入。

      例如:

      CEIL(4.485 * 100) / 100 will return 4.49
      FLOOR(4.485 * 100) / 100 will return 4.48
      

      【讨论】:

      • 这是真的,但问题是是否可以将 mysql 行为定义为向上或向下舍入一半。含义:1.5 半向下 = 1,1,5 半向上 = 2,CEIL 也会将 4.4 向上舍入为 5,这不是正确的答案
      【解决方案5】:

      根据Mysql docs

      • 对于精确值数字,ROUND() 使用“四舍五入”规则

      我也遇到过一种情况,我想知道为什么 Mysql 将 (1) 74,447 舍入到 74,4。这是因为它实际上是一个无穷长的小数,一个近似值数,而不是一个精确值数。我的解决方案是,我必须像这样使用ROUND() 3x:ROUND(ROUND(ROUND(value, 3), 2), 1)。第一次舍入确保它不再是一个近似值数(Mysql 使用“四舍五入到最接近的偶数”规则),而是一个精确值数,一个十进制的 3 数字。

      【讨论】:

        【解决方案6】:

        我有同样的问题,我尝试了各种自定义函数来模拟 PHP 的功能,上下舍入,但过了一段时间,我发现 MySQL 使用 float 和 doubles 字段产生不同的结果。

        这是我的测试。

        mysql> describe test_redondeo;
        +----------------+--------------+------+-----+---------+-------+
        | Field          | Type         | Null | Key | Default | Extra |
        +----------------+--------------+------+-----+---------+-------+
        | columna_double | double(15,3) | YES  |     | NULL    |       |
        | columna_float  | float(9,3)   | YES  |     | NULL    |       |
        | id             | int(11)      | NO   | PRI | NULL    |       |
        +----------------+--------------+------+-----+---------+-------+
        3 rows in set (0.01 sec)
        

        在字段类型中为 double,round 函数截断,但在 float 中向上舍入。

        mysql> select columna_double, columna_float, round ( columna_double, 2) as round_double, round ( columna_float, 2) as round_float from test_redondeo;
        +----------------+---------------+--------------+-------------+
        | columna_double | columna_float | round_double | round_float |
        +----------------+---------------+--------------+-------------+
        |        902.025 |       902.025 |       902.02 |      902.03 |
        +----------------+---------------+--------------+-------------+
        1 row in set (0.00 sec)
        

        如果您以任何方式使用 round() 对数字进行舍入自定义函数,请注意字段类型。

        希望这个测试对你有所帮助。

        【讨论】:

          猜你喜欢
          • 2011-03-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多