【发布时间】:2015-04-28 16:17:24
【问题描述】:
是否存在提供工程符号格式(作为字符串)的现有 Haskell 函数?
如果没有,我读到printf 可以通过向PrintfArg 添加实例来扩展。您认为这是一个好的解决方案吗?
我所说的工程符号是指指数是3的倍数的指数符号。
【问题讨论】:
标签: haskell floating-point formatting floating-point-precision scientific-notation
是否存在提供工程符号格式(作为字符串)的现有 Haskell 函数?
如果没有,我读到printf 可以通过向PrintfArg 添加实例来扩展。您认为这是一个好的解决方案吗?
我所说的工程符号是指指数是3的倍数的指数符号。
【问题讨论】:
标签: haskell floating-point formatting floating-point-precision scientific-notation
经过一番研究,我设法得到了我想要的。 只需几步即可获得工程格式的功能:
1.将指数与尾数分离
有必要将尾数中的指数分开。
函数decodeFloat(由base提供)解码一个浮点数并返回尾数和指数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 对大范围和大量值进行了一些测试。尽管值的差异非常小(在计算过程中由于精度而四舍五入?),但转换似乎运行良好。
但是,应该进行其他验证。
如果您发现这些功能有错误或改进,请分享。
【讨论】:
在Data.Text.Format 中有一个expt 函数可以帮助以这种方式格式化数字,但恐怕它在一个非常模糊的库中,你必须从Text 转换为String .
这似乎是唯一可用的,但你总是可以自己制作一个。
【讨论】:
expt 0 10 给出了"1e2",而 2 不是 3 的倍数。
我不知道标准函数。向printf 添加一些内容是一种方法,但使用起来会有点烦人(因为您需要为工程符号格式的数字添加一种新类型,并在传递数字之前将它们转换为这种类型)。只需编写一个类型为
showEngineer :: Double -> String
从长远来看,可能是一个更简单、更易读的解决方案。
【讨论】: