3

I am brand new to MySQL, so I apologize if some of this is relatively elementary.

I'm trying to write a stored function to round a digit to a certain number of significant figures. I have a function which, in theory, should work - I consulted another helpful StackOverflow to nail down the logic (Round to n Significant Figures in SQL). However, numbers between 1 and -1 (excluding 0, because I threw in the edge case catch) fail to round appropriately; for instance, a call of sfround(.00123456789, 5), which should yield 0.0012345, instead yields 0.0012345999712124467.

In an attempt to fix this, I was hoping to truncate digits between 1 and -1 (excluding 0) to the number of trivial zeros PLUS the number of necessary sig figs. In the code below, the IF clause is intended to carry out this truncation. However, I find that whenever I include this IF clause, it results in an error:

'Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE numDigits INT; SET numDigits = FLOOR(LOG10(ABS(number))); #total d' at line 5).

Even when I experimented with a very simple IF statement, such as

IF (5 = 5) THEN DECLARE dummyvar INT; END IF;

I still got the same error at this line, which leads me to think that I am missing something simple in my MySQL syntax for an IF clause.

DELIMITER $$

USE `vg_summary1`$$

DROP FUNCTION IF EXISTS `sfround`$$

CREATE FUNCTION 'sfround`(number FLOAT, sf INT) RETURNS FLOAT
DETERMINISTIC
BEGIN
     IF ((ABS(number) < 1) AND (ABS(number) > 0)) THEN            
        DECLARE numDigits INT;
        SET numDigits = FLOOR(LOG10(ABS(number)));     
        DECLARE trivialDigits INT;
        SET trivialDigits = 0;                         
                DECLARE placeholder FLOAT;
        SET placeholder = ABS(number);               

        WHILE placeholder < 1
        BEGIN
           placeholder = placeholder * 10;
           IF (placeholder < 1) THEN trivialDigits = trivialDigits + 1;    
           END IF;
        END;

        DECLARE keptDigits = trivialDigits + sf;

        DECLARE special_answer FLOAT;
        SET special_answer = ROUND(number, keptDigits-1-FLOOR(LOG10(ABS(number))));
        RETURN special_answer;

     END IF;

     DECLARE r FLOAT;

     SET r = CASE WHEN number = 0 THEN 0 ELSE ROUND(number, sf-1-FLOOR(LOG10(ABS(number)))) END;
     RETURN r;

   END$$

 DELIMITER ;

If anyone has any suggestions in terms of syntax errors or more logical ways to fix the original error, I would be very grateful to hear them! Thank you so much!

Bill Karwin
  • 538,548
  • 86
  • 673
  • 828
  • 1
    I edited to write stored function instead of UDF. MySQL uses the term [UDF for functions you write in C or C++ and compile as shared libraries to extend the MySQL Server](https://dev.mysql.com/doc/refman/8.0/en/adding-udf.html). They are not the same as [stored functions](https://dev.mysql.com/doc/refman/8.0/en/create-procedure.html). – Bill Karwin Jun 04 '19 at 00:22
  • Note that `RETURNS FLOAT` seems to be part of the (original) problem, here. `FLOAT` is a problematic data type, unrelated to MySQL, but instead for reasons inherent in the way single precision floating point numbers are represented in memory -- as approximate values. The return value of your function will be something that can necessarily be represented by the constraints of this data type. From none other than @BillKarwin, in fact... https://twitter.com/billkarwin/status/347561901460447232 – Michael - sqlbot Jun 04 '19 at 02:05

1 Answers1

1

See https://dev.mysql.com/doc/refman/8.0/en/declare.html

DECLARE is permitted only inside a BEGIN ... END compound statement and must be at its start, before any other statements.

Bill Karwin
  • 538,548
  • 86
  • 673
  • 828
  • Thank you so much, this fixed the problem! If anyone else is interested in using this workaround for rounding to sig figs, I fixed the DECLARE statements and another bug in my while loop (I was missing 'DO' after the entry condition) for the above code to run properly. – oliviaratliff Jun 04 '19 at 01:57