How to format float to omit zeros at the end of the fraction

I'd like to format a float so that it has at most 3 fractional digits, but if the last fractional digit(s) are 0 then omit them.

input : expected output
1.1234 : 1.123
1.120 : 1.12
1.100 : 1.1
1.0 : 1

%.3f always gives 3 fractional digits (1f.format("%.3f") = "1.000")
  • What I would do:
    Build a function doing a loop and check (and cut) last zero of %.3f formatted string.

  • Until some better idea comes up, these work:

    function formatShortFloat(value as Float) as String { // ciq_1_3_0
        var str = value.format(FLOAT_FORMAT_STRING_3);
        var chars = str.toCharArray(); // ciq_1_3_0
        var end;
        for (end = str.length() - 1; chars[end] == '0' && end > 0; --end) {
        }
        if (chars[end] == '.') {
            --end;
        }
        return str.substring(0, end + 1) as String;
    }
    

    or if need to work on very old devices < CIQ 1.3.0:

    function formatShortFloat(value as Float) as String { // code:-3,optimized code:+5,data:+5
        var str = value.format("%.3f");
        var end = str.length();
        var lastChar;
        do {
            lastChar = str.substring(end - 1, end);
            --end;
        } while ("0".equals(lastChar));
        if (".".equals(lastChar)) {
            --end;
        }
        return str.substring(0, end + 1) as String;
    }

  • Solution adapted from [https://stackoverflow.com/a/22885197]

    Probably more code than you'd like.

        // Count the number of decimal digits, disregarding trailing 0s
        // This won't work for if num.toString() happens to be converted
        // to exponential notation (e.g. "1.23e-50") but doesn't seem to happen
        // in Monkey C and it isn't a practical scenario anyway
        //
        // adapted from https://stackoverflow.com/a/22885197
        function numSignificantDecimalDigits(num as Numeric) as Number {
            var str = num.toString();
            var decimalIndex = str.find(".");
            if (decimalIndex == null) {
                return 0;
            }
    
            // obtain fractional part of the number
            var fractionalStr = str.substring(decimalIndex + 1, str.length());
            if (fractionalStr.toNumber() == 0) {
                return 0;
            }
    
            // prefix with "1" to handle leading zero
            var n = ("1" + fractionalStr).toNumber();
    
            while (n != 0 && n % 10 == 0) {
                n /= 10; // kill the 0s at the end of n;
            }
            return Math.floor(Math.log(n, 10)).toNumber(); // get the number of digits
        }
    
        function formatRemoveTrailingDecimalZeroes(num as Numeric, numDecimals as Numeric) as String {
            var sigDecimals = numSignificantDecimalDigits(num);
            var actualDecimals = sigDecimals < numDecimals ? sigDecimals : numDecimals;
            return num.format("%." + actualDecimals  + "f");
        }
    
        function test_numSignificantDecimalDigits() {
            System.println("numSignificantDecimalDigits(1): " + numSignificantDecimalDigits(1));
            System.println("numSignificantDecimalDigits(1.0): " + numSignificantDecimalDigits(1.0));
            System.println("numSignificantDecimalDigits(1.001): " + numSignificantDecimalDigits(1.001));
            System.println("numSignificantDecimalDigits(1.23450): " + numSignificantDecimalDigits(1.23450));
            System.println("numSignificantDecimalDigits(1.1234): " + numSignificantDecimalDigits(1.1234));
            System.println("numSignificantDecimalDigits(1.123): " + numSignificantDecimalDigits(1.123));
            System.println("numSignificantDecimalDigits(1.12): " + numSignificantDecimalDigits(1.12));
            System.println("numSignificantDecimalDigits(1.1): " + numSignificantDecimalDigits(1.1));
        }
    
        function test_formatRemoveTrailingDecimalZeroes() {
            System.println("formatRemoveTrailingDecimalZeroes(1, 3): " + formatRemoveTrailingDecimalZeroes(1, 3));
            System.println("formatRemoveTrailingDecimalZeroes(1.0, 3): " + formatRemoveTrailingDecimalZeroes(1.0, 3));
            System.println("formatRemoveTrailingDecimalZeroes(1.001, 3): " + formatRemoveTrailingDecimalZeroes(1.001, 3));
            System.println("formatRemoveTrailingDecimalZeroes(1.23450, 3): " + formatRemoveTrailingDecimalZeroes(1.23450, 3));
            System.println("formatRemoveTrailingDecimalZeroes(1.1234, 3): " + formatRemoveTrailingDecimalZeroes(1.1234, 3));
            System.println("formatRemoveTrailingDecimalZeroes(1.123, 3): " + formatRemoveTrailingDecimalZeroes(1.123, 3));
            System.println("formatRemoveTrailingDecimalZeroes(1.12, 3): " + formatRemoveTrailingDecimalZeroes(1.12, 3));
            System.println("formatRemoveTrailingDecimalZeroes(1.1, 3): " + formatRemoveTrailingDecimalZeroes(1.1, 3));
        }
    

    Output:

    numSignificantDecimalDigits(1): 0
    numSignificantDecimalDigits(1.0): 0
    numSignificantDecimalDigits(1.001): 3
    numSignificantDecimalDigits(1.23450): 4
    numSignificantDecimalDigits(1.1234): 4
    numSignificantDecimalDigits(1.123): 3
    numSignificantDecimalDigits(1.12): 2
    numSignificantDecimalDigits(1.1): 1
    formatRemoveTrailingDecimalZeroes(1, 3): 1
    formatRemoveTrailingDecimalZeroes(1.0, 3): 1
    formatRemoveTrailingDecimalZeroes(1.001, 3): 1.001
    formatRemoveTrailingDecimalZeroes(1.23450, 3): 1.235
    formatRemoveTrailingDecimalZeroes(1.1234, 3): 1.123
    formatRemoveTrailingDecimalZeroes(1.123, 3): 1.123
    formatRemoveTrailingDecimalZeroes(1.12, 3): 1.12
    formatRemoveTrailingDecimalZeroes(1.1, 3): 1.1

  • ...works for me...

            // TEST for shortening a FloatString without zeros at the end:
            var testFloat = 120.00000f ;
            System.println( cutFloatZeros(testFloat.format("%.3f")));
    

        function cutFloatZeros(floatFigure) as String {
            var floatStr = floatFigure;
             while ( (floatStr.substring(floatStr.length()-1, null )).equals("0") ) {
                floatStr = floatStr.substring(0, floatStr.length()-1);
            }
            if ( (floatStr.substring(floatStr.length()-1, null )).equals(".") ) {
                floatStr = floatStr.substring(0, floatStr.length()-1);
            }
             return floatStr;
        }
    

  • Yeah, this is the same algorithm as my second, except for 2 things:

    1. it only works on CIQ 3.3.2+ because you pass null to substring

    2. I think it's less efficient because it does lot more substrings

  • For what it’s worth, my solution is def more efficient if you’re on CIQ < 1.3.0. In all other cases, the efficiency is probably the same or worse (although you might be able to refactor it to make it more efficient, depending on how inefficient toNumber() is).

    I doubt efficiency is a huge deal for these functions anyway.