Вокруг числа до десятичного знака - PullRequest
0 голосов
/ 04 октября 2019

Я пытаюсь отобразить число с плавающей точкой, но округляю его, так что

f = 5.545

отображается как: 5.55, а

f = 5.544 

отображается как: 5.54

Я видел способ отображения только первых двух десятичных знаков, но я хочу, чтобы он был округлен.

Спасибо!

Ответы [ 2 ]

0 голосов
/ 04 ноября 2019

Так что я не совсем нашел реальное решение для этого, но я кое-что понял:

Printf уже выполняет свою работу, но не совсем так, как я хотел. Допустим, я хочу округлить 1.445, он покажет 1.44. Но если бы число было 1.446, то оно показало бы 1.45.

Не совсем то, что я хотел, но достаточно близко.

0 голосов
/ 04 октября 2019

Это происходит из-за того, как числа с плавающей запятой представлены в компьютере: они на самом деле в base-2, а не в base-10 (немного упрощенно, но достаточно хорошо). Как следствие, когда вы вводите 0.545, компьютер фактически записывает это как 0.5499999999... - очень близко к 0.545, но немного меньше. И поскольку он меньше 0.545, неудивительно, что он округляется до 0.54.

Если вам действительно нужно иметь точные числа с базовыми 10, вам следует использовать Decimal вместо Float или Double. Этот пакет специально заботится о представлении чисел с плавающей точкой в ​​base-10 без потерь.

x :: Decimal
x = 0.545

show x
> "0.545"

Предостережение заключается в том, что printf не поддерживает Decimal, поэтому вам придется отображать его с помощьюокругление по roundTo и преобразование в строку по show. Еще одно предостережение, что roundTo выполняет «округление банкира» - если последняя цифра равна пяти, она округляется до ближайшей даже цифры, поэтому нам нужно было бы противодействовать этому как особому случаю (я не могнайдите готовую к использованию функцию, которая округляется по арифметическим правилам):

displayDecimal :: Decimal -> String
displayDecimal x = show (rounded + compensate)
    where rounded = roundTo 2 x
          compensate = if (x - rounded) == 0.005 then 0.01 else 0

displayDecimal 0.545
> "0.55"

displayDecimal 0.5450000000001
> "0.55"

displayDecimal 0.544
> "0.54"

displayDecimal 0.5449999999999
> "0.54"

Однако, если вы просто хотите, чтобы это работало для чисел с тремя десятичными знаками , вы можете уйтипросто добавив очень маленькое значение перед округлением, как 0.00001. Это значение достаточно мало, чтобы не испортить ваши действительные числа, но достаточно велико, чтобы компенсировать расхождение между базой-2 и базой-10:

displayRounded :: Double -> String            
displayRounded x = printf "%.2f" (x + 0.00001)

displayRounded 0.544
> "0.54"

displayRounded 0.545
> "0.55"
...