7

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.

JeanJouX
  • 2,555
  • 1
  • 25
  • 37

3 Answers3

2

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.

JeanJouX
  • 2,555
  • 1
  • 25
  • 37
0

In Data.Text.Format there's an expt function that will help format numbers in that way, though it's in a hugely obfuscated library, I'm afraid, and you'll have to convert from Text to String.

It seems that this is the only one available, but you could always make one yourself.

AJF
  • 11,767
  • 2
  • 37
  • 64
  • This does not seem to conform to the specification, "whose exponent is a multiple of 3". Specifically, `expt 0 10` gives `"1e2"`, and 2 is not a multiple of 3. – Daniel Wagner Apr 28 '15 at 20:59
  • @DanielWagner that's odd; in the library, it says it's in enginerring notation. That might be a bug. – AJF Apr 29 '15 at 06:40
0

I don't know of a standard function. Adding something to printf would be one way to go, but it would be a bit annoying to use (since you would need to add a new type for engineering-notation-formatted numbers, and convert numbers to this type before handing them off). Simply writing a standalone function with a type like

showEngineer :: Double -> String

may be a simpler and more readable solution in the long-term.

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380