A formatação de números flutuantes do Bash printf
(com %f
ou %g
) de repente ficou completamente errada e mudou o tempo todo.
Um exemplo de saída:
$ export LC_ALL=C
$ printf '%g\n' 1
1.20739e+3531
$ printf '%g\n' 1
4.50784e-4778
$ printf '%g\n' 1
1.20739e+3531
$ printf '%g\n' 1
-2.71289e-809
$ printf '%g\n' 1
1.3505e+3136
$ printf '%g\n' 1
7.19546e+2880
$ printf '%f\n' 1
0.000000
$ printf '%f\n' 1
-19222373783767455764509969957314767706032565305205751517976389011586356910287283735873496366975075385910809025031233228909443235485943598697862160225065543796566324326303010027986380740507313362109478389280032720809755605977575591712977484833714421549464179548312816619669691695969675768618124142786377660052446990574866227828104911221712770547544994592495621702453486060768120985842267603349960735550433657827156688542311891238932963870595675569407105652838611000368894193915118759451716250477855842495787217908770520219596001805968923516834815448319714521179606192054239712352794281273655344703644255690153172028191401140768961585354430400117766273628103129509500449952562778071128862373050266582261154261590345666202936676378315868263637718127652344391988248019089245237502067305800855582602326233046151999935749805709388443164385685796904118233897433899895164143403147924128628840995216653448904704.000000
$ printf '%f\n' 1
-1535939692251970798780814048195400843655654544941633379309930935432267368465724365567117453392273362651582982424721184431169329486749020702578930669204206705946793193732917405530938783202632819503328225917797530425669645856818642162664722510713747202433401571296943178862058175872487260754269792220244257605097737000140353828109440097622967061126831273879953063844519410176251996107980841451192710472925184.000000
$ printf '%f\n' 1
-559936185544451048996856397218869061556420299014564433770484452018186365447891968084773838558135844864.000000
É o bash builtin que está sendo usado, como posso ver quando digito command -v printf
.
alias printf
diz "não encontrado". Tenho os binários em /bin/printf
e /usr/bin/printf
que se comportam normalmente. Comandos equivalentes do Awk e Python se comportam normalmente. Curiosamente, ao usar sh
, também está tudo bem.
Estou no Debian testing e tentei apt upgrade
reiniciar, mas o bug persiste.
A versão do Bash é GNU bash, version 5.2.32(1)-release (x86_64-pc-linux-gnu)
. Eu tentei apt reinstall bash
sem sucesso. Ontem por outro motivo eu fiz um apt-get dist-upgrade
.
O que eu fiz?! É selvagem.
[EDIT: depuração adicional abaixo]
- Executei um Memtest86+ por 12 passagens e não detectei nenhum erro.
- recompilado Bash 5.2.32 do GNU , ele tem o problema.
- tentado como outro usuário.
- Este bug foi relatado aqui: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1078556 . Acho que vou continuar este tópico com o sistema de relatórios de bugs do Debian.
Aqui está o conteúdo de /etc/apt/sources.list
:
deb https://deb.debian.org/debian/ testing main contrib non-free non-free-firmware
deb-src https://deb.debian.org/debian/ testing main contrib non-free non-free-firmware contrib
deb http://security.debian.org/debian-security testing-security main contrib non-free non-free-firmware contrib
deb-src http://security.debian.org/debian-security testing-security main contrib non-free non-free-firmware contrib
e também tenho /etc/apt/preferences.d/security
como sugerido em Melhores práticas para usuários de teste :
Package: src:chromium src:firefox src:firefox-esr src:linux src:linux-signed-amd64
Explanation: these packages are always security updates updated in unstable first
Pin: release a=/^(unstable|unstable-debug|buildd-unstable|buildd-unstable-debug)$/
Pin-Priority: 980
Parece que me deparei com um bug que afeta o Bash 5.2: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1078556
Conforme indicado por @StéphaneChazelas, uma correção foi feita no upstream .
No momento, preciso usar uma versão correta ou aplicar este patch .
Efetivamente, o bash 5.2 introduziu um bug em que em sistemas onde
strtold()
é considerado quebrado eprintf
usadouble
s em vez delong double
s para seu ponto flutuante como resultado, ele ainda acaba chamandoprintf()
com%Lf
como o formato. Esse bug em particular foi corrigido no branch devel , então será incluído no 5.3 , mas não no branch 5.2 (ou melhor, nenhum patch oficial foi lançado ainda, o bash não usa realmente o git).Agora, isso só deveria afetar sistemas com um arquivo "quebrado"
strtold()
.A verificação de um quebrado
strtold()
, que foi adicionada na versão 3.0 há 20 anos, menciona HP/UX onde, de acordo com o manual do gnulib, em algumas versõesstrtold()
retornou umstruct
em vez delong double
.Mas essa verificação em si foi quebrada, ela verificou que um arquivo C com:
Compilado corretamente (o que não aconteceria no HP/UX por causa do tipo do valor retornado), mas está chamando
strtold()
com parâmetros do tipo errado, o segundo é umchar*
em vez de umchar**
.Ambos são ponteiros, então você costumava usá-los com compiladores mais antigos, mas o gcc 14 se tornou mais rigoroso , pois
-Werror=incompatible-pointer-types
passou a ser habilitado por padrão.Então o resultado é que com o gcc 14, essa verificação falha e
strtold()
é considerada quebrada mesmo em sistemas onde não está, o que causa aquele outro bug.E mesmo com o outro bug corrigido, isso significa que
printf
ele reverte paradouble
s e acaba perdendo precisão.Esse bug também foi corrigido (silenciosamente) na versão devel muito antes, mas ainda não na versão 5.2.x.
Uma solução alternativa por enquanto é aplicar essas duas correções (ambas incluídas na mensagem ) e recompilar o pacote, ou compilá-lo com o gcc 13 ou anterior.