工程符号 Haskell
Engineering notation with Haskell
是否存在提供工程符号格式(作为字符串)的现有Haskell函数?
如果没有,我读到 printf
可以通过向 PrintfArg
添加一个实例来扩展。您认为这是一个好的解决方案吗?
通过工程符号,我指的是指数符号,其指数是 3 的倍数。
在 Data.Text.Format
中有一个 expt
函数可以帮助以这种方式格式化数字,但恐怕它在一个非常模糊的库中,你必须从 Text
到 String
.
似乎只有这一个可用,但您总是可以自己制作一个。
我不知道标准函数。添加一些东西到 printf
是一种可行的方法,但使用起来会有点烦人(因为你需要为工程符号格式的数字添加一个新类型,并在处理之前将数字转换为这种类型他们关闭)。只需编写一个类型为
的独立函数
showEngineer :: Double -> String
从长远来看,这可能是一个更简单、更易读的解决方案。
经过一番研究,我设法得到了我想要的东西。
只需几步即可获得工程格式的功能:
1.将指数与尾数分离
有必要将指数与尾数分开。
函数 decodeFloat
(由 base
提供)解码浮点数和 return 尾数和指数 的 2 次幂(mant2 * 2 ^ ex2).
2。获取以正确底数表示的尾数和指数
需要以 10 的 次方进行转换。这就是这个函数的作用。
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。将指数设置为 3
的倍数
函数ingen
测试指数整数除法的结果并对尾数和指数进行调整
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
以下是一些转换:
Prelude> ingen 10e7
(99.99999999999979,6)
Prelude> ingen 10e-41
(100.0,-42)
Prelude> ingen (-72364e81)
(-72.36399999999853,84)
我用quickCheck做了一些大范围、大数值的测试。转换 似乎 尽管存在 非常小的 值差异(由于精度而在计算期间四舍五入?)。
但是,还需要再验证一下。
如果您在这些功能中发现错误或改进,请分享。
是否存在提供工程符号格式(作为字符串)的现有Haskell函数?
如果没有,我读到 printf
可以通过向 PrintfArg
添加一个实例来扩展。您认为这是一个好的解决方案吗?
通过工程符号,我指的是指数符号,其指数是 3 的倍数。
在 Data.Text.Format
中有一个 expt
函数可以帮助以这种方式格式化数字,但恐怕它在一个非常模糊的库中,你必须从 Text
到 String
.
似乎只有这一个可用,但您总是可以自己制作一个。
我不知道标准函数。添加一些东西到 printf
是一种可行的方法,但使用起来会有点烦人(因为你需要为工程符号格式的数字添加一个新类型,并在处理之前将数字转换为这种类型他们关闭)。只需编写一个类型为
showEngineer :: Double -> String
从长远来看,这可能是一个更简单、更易读的解决方案。
经过一番研究,我设法得到了我想要的东西。 只需几步即可获得工程格式的功能:
1.将指数与尾数分离
有必要将指数与尾数分开。
函数 decodeFloat
(由 base
提供)解码浮点数和 return 尾数和指数 的 2 次幂(mant2 * 2 ^ ex2).
2。获取以正确底数表示的尾数和指数
需要以 10 的 次方进行转换。这就是这个函数的作用。
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。将指数设置为 3
的倍数函数ingen
测试指数整数除法的结果并对尾数和指数进行调整
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
以下是一些转换:
Prelude> ingen 10e7
(99.99999999999979,6)
Prelude> ingen 10e-41
(100.0,-42)
Prelude> ingen (-72364e81)
(-72.36399999999853,84)
我用quickCheck做了一些大范围、大数值的测试。转换 似乎 尽管存在 非常小的 值差异(由于精度而在计算期间四舍五入?)。
但是,还需要再验证一下。
如果您在这些功能中发现错误或改进,请分享。