7

when I do 5.2 - 2.3 in ghci I'll get 2.9000000000000004 instead of 2.9. Also such ugly (and for a human WRONG) results shows up on other places when working with Double or Float.

Why does this happen? (this is just for curiosity, not my real question)

My real question: How do I tell ghci to not do that, and show the results of Operations on Doubles just as any other programming language (and calculator) would and just as every 15 year old would write them?

This is just so annoying when I use ghci as a nice calculator and work on lists on which I perform such operations.

map ((-)2.3) [4.0, 3.8, 5.2, 6.4, 1.3, 8.3, 13.7, 9.0, 7.5, 2.4]
[-1.7000000000000002,-1.5,-2.9000000000000004,-4.1000000000000005,0.9999999999999998,-6.000000000000001,-11.399999999999999,-6.7,-5.2,-0.10000000000000009]

This just doesn't help when using the numbers on a piece of paper afterwards

Thanks in advance :)

4

4 回答 4

18

为什么会这样?

因为某些浮点数不能在不四舍五入的情况下用有限的位数表示。 浮点数的位数有限,它们不能准确地表示所有实数:当位数超过格式允许的位数时,剩余的将被省略 - 数字被四舍五入。

您可能应该阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic这个答案

于 2013-07-21T07:38:29.840 回答
8

我如何告诉 ghci 不要那样做,并像任何其他编程语言(和计算器)一样显示双精度运算的结果,就像每个 15 岁的孩子都会写它们一样?

由于这些结果是 GHCI(和您的标准计算器*)计算的实际结果,因此您无法更改结果的内部表示(请参阅 TNI 的答案)。由于您只想显示固定数量的小数,因此更多的是演示文稿(与printf("%f.2",...)C 相比)。

可以在https://stackoverflow.com/a/2327801/1139697中找到解决方案。它可以这样应用:

import Numeric
fixedN :: (RealFloat b) => Int -> b -> String
fixedN a b = showFFloat (Just a) b ""

map (fixedN 2 . (-)2.3) [4.0, 3.8, 5.2, 6.4, 1.3, 8.3, 13.7, 9.0, 7.5, 2.4]
-- result: ["-1.70","-1.50","-2.90","-4.10","1.00","-6.00",...]

请注意,如果您想继续计算,这将不可行。如果你想要精确的算术,你最好还是使用Rationals。不要忘记在这种情况下您的输入也应该是合理的。

* 是的,即使你的标准计算器也做同样的事情,你看不到它的唯一原因是固定的显示,它不能显示超过固定的小数位数。

于 2013-07-21T08:11:54.083 回答
3

我如何告诉 ghci 不要那样做,并像任何其他编程语言(和计算器)一样显示双精度运算的结果

你真的尝试过“任何其他编程语言”吗?还是你只是在欺负别人?

FWIW,这是使用 JVM 的语言的解释器的输出:

frege> 5.2 - 2.3
2.9000000000000004

在我看来,好像所有 JVM 语言都会得到相同的结果。而且由于 JVM 是用 C++ 编写的,因此结果很可能是相同的。而且由于大多数语言的运行时都是用 C/C++ 编写的,因此您很可能也使用这些语言获得相同的结果。除非它们是如此“用户友好”并且执行您没有要求的舍入。

于 2013-07-21T12:18:11.170 回答
3

浮点数的本质是它们不能准确地表示实数(也不是有理数)。Haskell 默认转换为字符串可确保在读回数字时获得完全相同的表示形式。如果您想以不同的方式打印数字,您可以制作自己的类型,以不同的方式显示数字。

类似(未经测试):

newtype MyDouble = MyDouble {getMyDouble :: Double}
            deriving (Eq, Ord, Num, Real, RealFrac, Fractional, Floating)
instance Show MyDouble where show = printf "%g" . getMyDouble
default (MyDouble)

这将创建该Double类型的副本,但具有Show仅打印几位小数的不同实例。default当存在歧义时,该声明使编译器选择这种类型。哦,要完成这项工作,您需要一些语言扩展。

您也可以尝试包装中的CReal类型。numbers

于 2013-07-21T10:02:44.783 回答