Да, это - способ, которым каждый обновляет пакеты через pkg_add. Вопрос был затронут здесь прежде: https://serverfault.com/questions/214611/handling-freebsd-package-upgrades-using-pkg-add.
] Ваш вопрос - "Почему это происходит?", но ваш неявный вопрос (на который обращались другие): "Как я могу это исправить?" Вы придумали подход, который вы подняли в комментарии:[
] [] [] []Так что если я умножу его до 1000, чтобы устранить точку, я смогу получить точный результат, не так ли?[
] [
]Да. Ну, 10000, так как у тебя четыре десятичных разряда. Подумайте об этом: [
] [awk '{ s+=$1*10000; print $1, s/10000 }'
]
[] К сожалению, это не сработает, потому что повреждение уже произошло.
как только мы интерпретируем токен (строку) как десятичное число.
Например, []printf "%.20f\n"[
] показывает, что входные данные []0.4157[
].
на самом деле интерпретируется как 0.415700000000001394.
В этом случае, умножая на 10000, вы получите то, что ожидали: 4157.
Но, например, []0.5973[
] = 0.597300000000005311,
и умножая это на 10000, получаем 5973.000000000090949470.[
]Поэтому вместо этого пытаемся[
] [awk '{ s+=int($1*10000); print $1, s/10000 }'
]
[]перевести числа, которые "должны быть" целыми числами (например, 5973.000000000090949470).
в соответствующие целые числа (5973).
Но это не удается, так как иногда ошибка преобразования отрицательна;
например, []0.7130[
] равно 0.71299999999999996714.
А функции [] awk[
] [] int([] expr[])[
] усекают (в сторону нуля).
вместо округления, так что []int(7129.99999999)[
] - это 7129. [
] Так что, когда жизнь дает лимоны, вы делаете лимонад.
А когда инструмент дает усеченную функцию, вы округляетесь, добавляя 0.5.
7129.999999+0.5≈7130.49999999, и, конечно, [] int(7130.49999999)[
] равно 7130.
Но помните: []int()[
] усекает [] в сторону нуля[], и на вашем входе есть отрицательные числа.
Если вы хотите округлить -7129.999999 до -7130,
вам нужно [][]вычесть[][] 0.5, чтобы получить -7130.49999999.
Итак, [
awk '{ s+=int($1*10000+($1>0?0.5:-0.5)); print $1, s/10000 }'
]
[], которая добавляет -0.5 к []$1*10000 [
], если []$1[
] ≤ 0. [
Для этого можно использовать программу, специально разработанную для работы с арифметическими операциями типа bc
:
$ awk '{printf "%s + ",$1}' file | sed 's/\+ $/\n/' | bc
0
Если, как кажется, у вас фиксированное количество знаков после запятой, вы можете просто удалить их, чтобы работать с целыми числами, а затем снова добавить в конце:
$ awk '{sub("0.","",$1);s+=$1;}END{print s/10000}' file
0
или
$ perl -lne 's/0\.//; $s+=$_; END{print $s/10000}' file
0
Большинство версий awk
имеют команду printf
. Вместо
print $1,s
используйте
printf "%.4f %.4f\n",$1,s
и выходы будут округлены до 4 знаков после запятой. Таким образом, вы не увидите большинства ошибок округления.
Это не awk
уникальная проблема, это еще и проблемы с другими языками программирования. Пример с perl
:
$ perl -anle '$sum+=$F[0]}{print $sum' file
-5.55111512312578e-17
Это проблема представления бесконечного ряда для базы 2 с использованием конечного числа двоичных разрядов. Числа с плавающей точкой не являются целыми числами. Для хранения чисел с плавающей точкой может потребоваться бесконечный объем памяти.
Вы можете прочитать эту статью , чтобы понять больше.
Это происходит потому, что компьютер имеет ограниченную точность только при работе с числами. А доступная точность использует двоичный формат для представления числа.
Это делает числа, которые кажутся тривиальными для записи в нашей десятичной системе, могут быть представлены только как приближение (см. запись в Википедии по этому поводу): например, 0,1
(как в 1/10
) действительно хранится на компьютере как что-то вроде 0,100000001490116119384765625
.
Таким образом, все ваши числа действительно обрабатываются только приближением (если только вам не повезло и у вас есть числа вроде 0,5
, которые могут быть представлены точно ).
Суммирование всех этих приблизительных чисел может в конечном итоге привести к ошибке ! = 0
.