Engineering notation with Haskell
G

3

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.

Gallnut answered 28/4, 2015 at 16:17 Comment(0)
G
3

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.

Gallnut answered 9/5, 2015 at 7:28 Comment(1)
You could add 1 additional step and give it the desired precision (number of digits after decimal) and then do an according round. At least in my case, you only need engineering mode for display purposes....Planoconcave
I
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.

Ingaingaberg answered 28/4, 2015 at 17:20 Comment(2)
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.Occupier
@DanielWagner that's odd; in the library, it says it's in enginerring notation. That might be a bug.Ingaingaberg
O
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.

Occupier answered 28/4, 2015 at 21:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.