Значение округления от половины до 2 знаков после запятой в Go - PullRequest
0 голосов
/ 09 октября 2018

Округление положительного значения (пример здесь: 1.015), половина до двух десятичных знаков, используя math.Round() в Go:

fmt.Println(math.Round(1.015*100) / 100)

Go Playground

Iполучил: 1.02.Это правильно.

Но когда я использовал функцию для выполнения той же работы:

func RoundHalfUp(x float64) float64 {
    return math.Round(x*100) / 100
}

Go Playground

Я получил 1.01.

Что не так с функцией RoundHalfUp?

1 Ответ

0 голосов
/ 09 октября 2018

Спецификация языка программирования Go

Константы

Числовые константы представляют собой точные значения произвольной точности и не переполняются.

Ограничение реализации: Хотя числовые константы имеют произвольную точность в языке, компилятор может реализовать их, используя внутреннее представление с ограниченной точностью.Тем не менее, каждая реализация должна:

  • Представлять константы с плавающей точкой, включая части комплексной константы, с мантиссой не менее 256 бит и двоичным показателем
    со знаком не менее 16биты.
  • Округление до ближайшей представимой константы, если невозможно представить числовую или комплексную константу из-за ограничений по точности.

Эти требования применяются как к литеральным константам, так и крезультат вычисления константных выражений.

константные выражения

константные выражения могут содержать только постоянные операнды и вычисляются во время компиляции.

Постоянные выражения всегда вычисляются точно;промежуточные значения и сами константы могут требовать точности, значительно превышающей поддерживаемую любым предварительно объявленным типом в языке.

Ограничение реализации: компилятор может использовать округление при вычислении нетипизированных выражений с плавающей точкой или сложных константных выражений;см. ограничение реализации в разделе о константах.Это округление может привести к тому, что константное выражение с плавающей точкой будет недопустимым в целочисленном контексте, даже если оно будет целочисленным при вычислении с бесконечной точностью, и наоборот.


Реализация RoundHalfUp функция, подобная компилятору Go, для math.Round(1.015*100) / 100.1.015*100 - нетипизированное выражение константы с плавающей точкой.Используйте пакет math/big с точностью не менее 256 бит.Go float64 (64-битная IEEE-754 с плавающей запятой) имеет точность 53 бита.

Например, с 256 битами точности (постоянное выражение),

package main

import (
    "fmt"
    "math"
    "math/big"
)

func RoundHalfUp(x string) float64 {
    // math.Round(x*100) / 100
    xf, _, err := big.ParseFloat(x, 10, 256, big.ToNearestEven)
    if err != nil {
        panic(err)
    }
    xf100, _ := new(big.Float).Mul(xf, big.NewFloat(100)).Float64()
    return math.Round(xf100) / float64(100)
}

func main() {
    fmt.Println(RoundHalfUp("1.015"))
}

Playground: https://play.golang.org/p/uqtYwP4o22B

Вывод:

1.02

Если мы используем только 53 бита точности (float64):

xf, _, err := big.ParseFloat(x, 10, 53, big.ToNearestEven)

Детская площадка: https://play.golang.org/p/ejz-wkuycaU

Выход:

1.01
...