Странное поведение float64 в Go

Проверив поведение float64, я обнаружил следующее.

  • Вывод 6-значной переменной float64 равен 100000.
  • Выход 7-значной переменной float64 равен 1e+06.
package main

import "fmt"

func main() {
    var a float64 = 100000
    fmt.Println(a) // output: 100000

    var b float64 = 1000000
    fmt.Println(b) // output: 1e+06 (expected: 1000000)
}

Может кто-нибудь помочь мне, почему вывод отличается в зависимости от количества цифр.

🤔 А знаете ли вы, что...
Go поддерживает множество платформ, включая Windows, Linux и macOS.


56
1

Ответ:

Решено

Вы наблюдаете поведение из-за форматирования Go по умолчанию для чисел с плавающей запятой. При выводе float64 Go пытается кратко представить число. Он печатает полное значение для меньших чисел (например, 100000). Однако как только в числе появляется больше цифр (например, 1000000), Go переключается на научную запись (1e+06).

Если вы хотите, чтобы полное значение печаталось без научных обозначений, вы можете использовать fmt.Printf с директивой форматирования:

package main

import "fmt"

func main() {
    var a float64 = 100000
    fmt.Printf("%.f\n", a)
    var b float64 = 1000000
    fmt.Printf("%.f\n", b)
}

Это напечатает оба числа без научных обозначений:

100000
1000000

Try on godbolt.org

Соответствующая функция, которая принимает это решение в научной записи, в исходном коде по адресу go.dev/src/strconv/ftoa.go по состоянию на 22 августа 2024 г.:

func formatDigits(dst []byte, shortest bool, neg bool, digs decimalSlice, prec int, fmt byte) []byte {
    switch fmt {
    case 'e', 'E':
        return fmtE(dst, neg, digs, prec, fmt)
    case 'f':
        return fmtF(dst, neg, digs, prec)
    case 'g', 'G':
        // trailing fractional zeros in 'e' form will be trimmed.
        eprec := prec
        if eprec > digs.nd && digs.nd >= digs.dp {
            eprec = digs.nd
        }
        // %e is used if the exponent from the conversion
        // is less than -4 or greater than or equal to the precision.
        // if precision was the shortest possible, use precision 6 for this decision.
        if shortest {
            eprec = 6
        }
        exp := digs.dp - 1
        if exp < -4 || exp >= eprec {
            if prec > digs.nd {
                prec = digs.nd
            }
            return fmtE(dst, neg, digs, prec-1, fmt+'e'-'g')
        }
        if prec > digs.dp {
            prec = digs.nd
        }
        return fmtF(dst, neg, digs, max(prec-digs.dp, 0))
    }

    // unknown format
    return append(dst, '%', fmt)
}

Интересные вопросы для изучения

Как определить оболочку типа для ObjectID примитива?Область видимости и несколько возвращаемых значенийКак мне настроить освобождаемый кеш в Golang (т. е. я могу освободить память при обнаружении нехватки памяти)?Как jackc/pgx работает с несколькими хостамиМожно ли использовать делегированный доступ с конфиденциальным клиентом в библиотеке аутентификации Microsoft?Влияет ли знак на точность и точность чисел с плавающей запятой?Можно ли с уверенностью предположить, что 32-битные числа с плавающей запятой можно напрямую сравнивать друг с другом, если значение соответствует мантиссе?Разница между 0f и 0 на Vector2 и Vector3 в UnityПочему я получаю большие случайные значения при использовании scanf("%f", &intVar)? Как входные данные с плавающей запятой преобразуются в эти значения?Почему коэффициент разницы, где h = FLT_MIN, всегда равен 0? FLT_MIN теряется в неточностях с плавающей запятой?