Home > Enterprise >  Conditional width and precision using printf
Conditional width and precision using printf

Time:07-13

I'm want to print as follows:

1.0 -> 1
18.0 -> 18
370.0 -> 370
1.2 -> 1.2
3.4 -> 3.4

In other words, print as a whole number when the mantissa is 0.0, otherwise print as a float with precision 1. I can check if the float is actually an integer and print accordingly, but is there a way to make printf do it? I checked that printf "/" 10.0 prints 10.0, so, it's not obvious.

CodePudding user response:

Based on the Hackage docs, you may not be able to achieve this with printf directly. Taken from the docs:

Haskell printf will place a zero after a decimal point when possible.

If you absolutely require the trailing .0 to be stripped you may need to look at an alternate means of formatting your data. As a quick and dirty hack, you could use floatToDigits from Numeric:

import Numeric (floatToDigits)
showFloat :: RealFloat a => a -> String
showFloat n = (concat $ map show prefix) <>
              separator                  <>
              (concat $ map show suffix)
  where
    (digits, precision) = floatToDigits 10 n
    (prefix, suffix)    = splitAt precision digits
    separator           = if null suffix then "" else "."

main = do
  putStrLn $ showFloat (0.123 :: Float) -- 0.123
  putStrLn $ showFloat (1.230 :: Float) -- 1.23
  putStrLn $ showFloat (12.30 :: Float) -- 12.3
  putStrLn $ showFloat (123.0 :: Float) -- 123

CodePudding user response:

%g uses the shortest representation.

Floating-point numbers usually aren't stored as a number in base 10, but 2 (performance, size, practicality reasons). However, whatever the base of your representation, there will always be rational numbers that will not be expressible in some arbitrary size limit for the variable to store them.

If the shortest representation has more than 16 digits, printf will shorten the number string by cutting cut the 2 digit at the very end, leaving you with 3122.550000000000, which is actually 3122.55 in the shortest form, explaining the result you obtained.

In general, %g will always give you the shortest result possible, meaning that if the sequence of digits representing your number can be shortened without any loss of precision, it will be done.

#include <stdio.h>

int main(void) {
    double x = 123.0;
    printf("%e\n", x);
    printf("%f\n", x);
    printf("%g\n", x);

    double y = 123.10;
    printf("%e\n", y);
    printf("%f\n", y);
    printf("%g\n", y);
}

Output:

1.230000e 02
123.000000
123

1.231000e 02
123.100000
123.1
  • Related