Search code examples
haskellfloating-pointformattingfloating-point-precisionscientific-notation

Engineering notation with Haskell


Is there a existing Haskell function which provide an engineering notation formatting (as String)?

If not, I read that printf can be extended by adding an instance to PrintfArg. Do you believe this is a good solution ?


By engineering notation, I mean an exponent notation whose exponent is a multiple of 3.


Solution

  • After some research, I manage to get what I want. The function to get the engineering format work in a few steps :

    1. Dissociate the exponent from the mantissa

    It's necessary to get the exponent apart of the mantissa. The function decodeFloat (provided by base) decode a floating point number and return both the mantissa and the exponent in power of 2 (mant2 * 2 ^ ex2).

    2. Get the mantissa and exponent expressed in the correct base

    A conversion in power of 10 is required. This is the role of this function.

    decompose :: Double -> (Double,Int)
    decompose val = if mant2 > 0 
                         then (mant10,ex10)
                         else (-mant10,ex10)
      where
            (mant2,ex2) = decodeFloat val
            res = logBase 10 (fromIntegral (abs mant2)::Double) + logBase 10 (2 ** (fromIntegral ex2::Double)) 
            ex10 = floor res
            mant10 = 10**(res - (fromIntegral ex10::Double))
    

    3. Set the exponent at a multiple of 3

    The function ingen test the result of the integer division of the exponent and perform the adjustement on mantissa and exponent.

    ingen :: Double -> (Double,Int)
    ingen val 
      | mod ex 3 == 0 = (mant,ex)
      | mod ex 3 == 1 = (mant*10,ex-1)
      | mod ex 3 == 2 = (mant*100,ex-2)
      where
            (mant,ex) = decompose val
    

    Here are some conversions :

    Prelude> ingen 10e7
    (99.99999999999979,6)
    Prelude> ingen 10e-41
    (100.0,-42)
    Prelude> ingen (-72364e81)
    (-72.36399999999853,84)
    

    I performed some tests with quickCheck on a wide range and a large amount of value. The conversion seems to work well despite a very small difference of value (rounding during the calculations due to the precision?).

    However, another verifications should be made.

    If you find a mistake or an improvement in these functions, please share.