Tipo long double

1. Tipo long double

José
DeuRuimDotCom

(usa Linux Mint)

Enviado em 04/09/2019 - 00:01h

Fiz uma pesquisa na documentação a respeito do tipo "long double" em C e verifiquei que, para ler e escrever seus valores, devo usar o formato "%Lf".
Todavia, ao usar esse tipo em um programinha simples, os valores não são lidos, quando compilado pelo Dev-C++; são lidos quando utilizado o CodeBlocks/GNU-GCC, porém operações aritméticas básicas sobre eles retornam valores absurdos, ou 0.0, ou enormes, com dezenas de dígitos.
Até mesmo valores de outros tipos parecem ser afetados, por ex.:
printf("%Lf :: %d", x, a), para "long double x = 3.000000" e "int a = 1", imprime 300000000000000000000000000000.000 :: 4194303.
Alguém saberia explicar por quê? Será estouro da pilha ou algo assim?


  


2. Re: Tipo long double

Paulo
paulo1205

(usa Ubuntu)

Enviado em 04/09/2019 - 01:58h

Você pode mostrar a listagem exata do trecho de código em questão?

Você falou em Dev-C++ e Code::Blocks. Está usando Windows? Tentou em outro sistema (e.g. Linux, MacOS/X, iOS, Android etc.)?

Na minha máquina (Ubuntu 18.04 64bits), o seguinte código funcionou como esperado.
#include <stdio.h>

int main(void){
long double x=3.0L;
int a=1;
printf("%Lf :: %d\n", x, a);
printf("%zu %p :: %zu %p\n", sizeof x, &x, sizeof a, &a);
}

3.000000 :: 1
16 0x7fff69d65af0 :: 4 0x7fff69d65aec



... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)


3. Re: Tipo long double

José
DeuRuimDotCom

(usa Linux Mint)

Enviado em 04/09/2019 - 16:33h

paulo1205 escreveu:
Você falou em Dev-C++ e Code::Blocks. Está usando Windows? Tentou em outro sistema (e.g. Linux, MacOS/X, iOS, Android etc.)?

Na verdade, testei primeiramente no Code::Blocks que tenho instalado sobre meu LinuxMint/Tessa, compilador padrão GNUGCC. Diante das incongruências, recorri ao Dev-C++ que instalara no computador de minha mãe, rodando Windows 7.

paulo1205 escreveu:
Você pode mostrar a listagem exata do trecho de código em questão?

Não foi num contexto de código específico, apenas estava fuçando as especificidades desse tipo long double mesmo. Fiz aqui rapidamente um exemplo em que houve erros de leitura e saída em matrizes:

printf( "\tDigite o numero de linhas: " ); scanf( " %u", linha ); fflush( stdin );
printf( "\tDigite o numero de colunas: " ); scanf( " %u", coluna ); fflush( stdin );
if( ( M = calloc( linha, sizeof *M ) ) == NULL )
printf( "\tERRO EM ALOCACAO DE MEMORIA." );
for( i = 0; i < linha; i++ )
{
if( ( M[i] = calloc( coluna + 1, sizeof **M ) ) == NULL )
printf("\tERRO EM ALOCACAO DE MEMORIA.");
for( j = 0; j < coluna; j++ )
{
printf( "\n\tDigite o %do. coeficiente da %da. equacao: ", j+1, i+1 );
scanf( " %Lf", &M[i][j] ); fflush( stdin );
}
printf( "\n\tDigite o resultado da %da. equacao: ", i+1 );
scanf( " %Lf", &M[i][j] ); fflush( stdin );
}
for( i = 0; i < linha; i++ )
{
printf( "\n\t" );
for( j = 0; j < coluna - 1; j++ )
printf( "(%Lf)*X%d + ", M[i][j], j );
printf("(%Lf)*X%d = ", M[i][j], j ); printf( "%Lf", M[i][++j] );

Exemplo de retorno desse comando: printf("(%Lf)*X%d = ", M[i][j], j ) -> (8.000000)*X4194304, onde deveria ser j = 1, isto é: (8.000000)*X1.
Se se fazem operações aritméticas simples sobre os elementos das matrizes acontece coisas desse tipo: M[1][2]*M[2][3] -> 13540000000000000000000000000000000000000000000.000000, onde deveria ser (3*4) -> 12.
Isso no CodeBlocks. No Dev-C++ todos os retornos são zerados.
Ahhh, e quando usado o tipo simplesmente "double", funciona como esperado.








4. Re: Tipo long double

Paulo
paulo1205

(usa Ubuntu)

Enviado em 04/09/2019 - 19:35h

Eu raramente usei long double, mas nunca tive problemas nas poucas vezes em que usei tal tipo.

Pelo sintoma que você descreveu, parece-me que você pode estar com um problema de alinhamento de dados em memória, particularmente quando usa a pilha, na passagem de argumentos para funções, especialmente printf(), que não usa protótipo, mas sim as regras para argumentos variadicos, cujo acesso é controlado pela string de formatação.

Experimente separar aquela chamada a printf() em que você imprime primeiro um valor do tipo long double e depois um inteiro em duas chamadas, imprimindo um de cada vez, e diga se o resultado foi melhor.


Quando postar código, procure postá-lo de um modo mais completo, incluindo as declarações de todas as variáveis que nele são usadas. Inclua também o código que reflete o comportamento que você entende estar errado (por exemplo, você falou sobre um produto estar sendo impresso com erro, mas não mostrou como está chamando a função de impressão, de modo que não dá para nem para especular se o erro é no cálculo ou na impressão).

Outras informações que podem ser úteis: versões e procedências dos compiladores, se são de 32 bits ou 64 bits, e opções de compilação usadas em cada caso.


... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)


5. Re: Tipo long double

José
DeuRuimDotCom

(usa Linux Mint)

Enviado em 05/09/2019 - 14:32h

paulo1205 escreveu:
Experimente separar aquela chamada a printf() em que você imprime primeiro um valor do tipo long double e depois um inteiro em duas chamadas, imprimindo um de cada vez, e diga se o resultado foi melhor.

Testei assim, imprimindo um de cada vez: printf(int), em seguida printf(long double+int) e novamente printf(int). Os printf(int) retornaram o valor exato do inteiro e permaneceu a inconsistência no printf(long double+int).

paulo1205 escreveu:
Quando postar código, procure postá-lo de um modo mais completo, incluindo as declarações de todas as variáveis que nele são usadas. Inclua também o código que reflete o comportamento que você entende estar errado (por exemplo, você falou sobre um produto estar sendo impresso com erro, mas não mostrou como está chamando a função de impressão, de modo que não dá para nem para especular se o erro é no cálculo ou na impressão).

Como disse, o problema surgiu num contexto de testes do tipo "long double" e não em um programa específico. Mas tem razão, sim. No código que postei, as declarações foram assim: "unsigned int linha, coluna; int i,j; long double** M" (Obs.: Se coloco apenas "double** M", funciona normalmente).
O erro na multiplicação que citei provavelmente foi na própria operação, uma vez que se deu num contexto simples de multiplicação de matrizes e a impressão foi dos valores da matriz produto, construída à parte. (Quando, sem aritmética envolvida, simplesmente se atribui um long double a dada variável e se imprime o valor, a impressão sai exata.)
O mesmo problema ocorreu quando testei a eliminação gaussiana para sistemas lineares.
Usei na maioria códigos extraídos de páginas confiáveis (por isso não quis postar aqui, porque não são meus e eu não lembrava a fonte exata de cada qual) e posso garantir que fiz vários testes e também com o tipo "double" simples, o qual funcionou perfeitamente.

paulo1205 escreveu:
Outras informações que podem ser úteis: versões e procedências dos compiladores, se são de 32 bits ou 64 bits, e opções de compilação usadas em cada caso.

Isso talvez seja importante mesmo. É que eu não sou do ramo da computação, sou mais um curioso, e raramente uso algo que não seja o default da aplicação. Mas vamos lá:
O CodeBlocks eu baixei direto da página da IDE na internet esse arquivo aqui: codeblocks_17.12-1_i386_stable.tar.xz (Corrigindo: codeblocks_17.12-1_amd64_stable.tar.xz). Nos settings, apenas diz que o compilador é o GNU GCC.
O Dev-C++, instalado sobre Windows 7, diz usar o TDM-GCC 4.9.2 64-bit release.
Desde já, grato pela atenção.




6. Re: Tipo long double

Paulo
paulo1205

(usa Ubuntu)

Enviado em 05/09/2019 - 22:22h

DeuRuimDotCom escreveu:

Testei assim, imprimindo um de cada vez: printf(int), em seguida printf(long double+int) e novamente printf(int). Os printf(int) retornaram o valor exato do inteiro e permaneceu a inconsistência no printf(long double+int).


Pois é... Isso sugere mesmo algo na linha de problema de alinhamento de dados ou no cálculo do tamanho efetivo dos dados do tipo long double.

Eu não tenho como comprovar (pelo menos não agora), mas eu suspeito que o seu código esteja considerando cada elemento com um tamanho, mas outras partes, como a implementação de printf() estejam usando um tamanho diferente (por exemplo, uma com 16 e outra com 12, ou vice-versa).

Eu não sei o que poderia estar causando essas diferenças de tamanho e/ou alinhamento, mas suspeito que possa ser algo afetado por opções de compilação, tais como -malign-double/-mno-align-double ou -m96bit-long-double/-m128bit-long-double, entre outras. Veja que interessante o comentário referente a essas duas últimas (grifos meus).

Warning: if you override the default value for your target ABI , the structures and arrays containing "long double" variables will change their size as well as function calling convention for function taking "long double" will be modified. Hence they will not be binary compatible with arrays or structures in code compiled without that switch.


Talvez, ao brincar com algumas dessas opções, você obtenha melhores resultados.

Outra possível fonte de erros é você misturar bibliotecas de procedências diferentes, que utilizem representações distintas para os mesmos tipos de dados. Por exemplo, o default em produtos da Microsoft é usar long double idêntico a double (ambos com 64 bits), então você teria de seguir a mesma convenção, ao compilar seu programa, caso use alguma biblioteca nativa da Microsoft que espere essa representação, ainda que use um compilador que não é dela para compilar o seu código.


... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)


7. Re: Tipo long double

José
DeuRuimDotCom

(usa Linux Mint)

Enviado em 07/09/2019 - 02:17h

paulo1205 escreveu:
Eu não sei o que poderia estar causando essas diferenças de tamanho e/ou alinhamento, mas suspeito que possa ser algo afetado por opções de compilação, tais como -malign-double/-mno-align-double ou -m96bit-long-double/-m128bit-long-double, entre outras.

Testei-as mas sem modificações relevantes. Me parece que o problema é no printf mesmo.
Achei aqui https://stackoverflow.com/questions/13516476/long-double-gcc-specific-and-float128 uma explicação:

"ANSI C, however, requires that code which calls printf must know which arguments are double and which are long double; a lot of code--if not a majority--of code which uses long double but was written on platforms where it's synonymous with double fails to use the correct format specifiers for long double values."




8. Re: Tipo long double

José
DeuRuimDotCom

(usa Linux Mint)

Enviado em 07/09/2019 - 02:35h

Em tempo, aconteceu uma mensagem de erro que antes não aparecia:

free(): invalid pointer
Abortado (imagem do núcleo gravada)

Ocorreu durante a execução do seguinte código:

void ExibirMatriz( long double** matriz, unsigned int nLin, unsigned int nCol )
{
int linha, coluna;
for( linha = 0; linha < nLin; linha++ )
{
printf( "\n\t" );
for( coluna = 0; coluna < nCol; coluna++ )
printf( "\t%Lf ", matriz[linha][coluna] );
}
}

long double* FatoracaoLU( long double** matriz, unsigned int tamanho )
{
long double* X = calloc( tamanho, sizeof *X ), Y[tamanho], **Lower = NULL;
int linha, coluna, pivo;

if( ( Lower = calloc( tamanho, sizeof *Lower ) ) == NULL )
{
printf( "\n\t_____ ERRO EM ALOCACAO DE MEMORIA _____" ); return NULL;
}
for( pivo = 0; pivo < tamanho; pivo++)
if( ( Lower[pivo] = calloc( tamanho - 1, sizeof **Lower ) ) == NULL )
{
printf( "\n\t_____ ERRO EM ALOCACAO DE MEMORIA _____" ); return NULL;
}
for( pivo = 0; pivo < tamanho - 1; pivo++ )
{
for( linha = pivo + 1; linha < tamanho; linha++ )
{
if( matriz[pivo][pivo] == 0 )
{
for(pivo = 0; pivo < tamanho; pivo++)
free(Lower[pivo]);
free(Lower);
free(X);
return NULL;
}
Lower[linha][pivo] = matriz[linha][pivo]/matriz[pivo][pivo];
for( coluna = pivo; coluna < tamanho; coluna++ )
matriz[linha][coluna] += - Lower[linha][pivo]*matriz[pivo][coluna];
}
}
for( pivo = 0; pivo < tamanho; pivo++ )
Lower[pivo][pivo] = 1.0;
for( linha = 0; linha < tamanho - 1; linha++ )
for( coluna = tamanho - 1; coluna > linha; coluna-- )
Lower[linha][coluna] = 0.0;
printf( "\n\n\tMatriz L: " ); ExibirMatriz( Lower, tamanho, tamanho - 1 );

printf( "\n\n\tMatriz U: " ); ExibirMatriz( matriz, tamanho, tamanho - 1 );
//A EXECUÇÃO FOI ATÉ AQUI, EM VERDADE, EXIBIU APENAS PARTE DA MATRIZ E RETORNOU O ERRO.

for( linha = 0, coluna = 0; linha < tamanho; linha++, coluna = 0 )
{
Y[linha] = 0.0;
while( coluna < linha )
{
Y[linha] += Lower[linha][coluna]*Y[coluna];
coluna++;
}
if( Lower[linha][coluna] == 0 )
{
for(pivo = 0; pivo < tamanho; pivo++)
free(Lower[pivo]);
free(Lower);
free(X);
return NULL;
}
Y[linha] = ( matriz[linha][tamanho] - Y[linha] )/Lower[linha][coluna];
}

for( linha = tamanho - 1, coluna = tamanho - 1; 0 <= linha; linha--, coluna = tamanho - 1 )
{
X[linha] = 0.0;
while( coluna > linha )
{
X[linha] += matriz[linha][coluna]*X[coluna];
coluna--;
}
if( matriz[linha][coluna] == 0 )
{
for(pivo = 0; pivo < tamanho; pivo++)
free(Lower[pivo]);
free(Lower);
free(X);
return NULL;
}
X[linha] = ( Y[linha] - X[linha] )/matriz[linha][coluna];
}
for(pivo = 0; pivo < tamanho; pivo++)
free(Lower[pivo]);
free(Lower);
return X;
}

Com o tipo "double", simples, o erro não acontece com esse código e a fatoração é feita corretamente (testei-a já dezenas de vezes!).


9. Re: Tipo long double

Paulo
paulo1205

(usa Ubuntu)

Enviado em 09/09/2019 - 01:28h

Algumas coisas no código não estão combinando.

DeuRuimDotCom escreveu:

 long double* FatoracaoLU( long double** matriz, unsigned int tamanho )
{
long double* X = calloc( tamanho, sizeof *X ), Y[tamanho], **Lower = NULL;
int linha, coluna, pivo;

if( ( Lower = calloc( tamanho, sizeof *Lower ) ) == NULL )
{
printf( "\n\t_____ ERRO EM ALOCACAO DE MEMORIA _____" ); return NULL;
}
for( pivo = 0; pivo < tamanho; pivo++)
if( ( Lower[pivo] = calloc( tamanho - 1, sizeof **Lower ) ) == NULL )
{
printf( "\n\t_____ ERRO EM ALOCACAO DE MEMORIA _____" ); return NULL;
}

/* ... bloco suprimido ... */

for( pivo = 0; pivo < tamanho; pivo++ ) /* Possivelmente você quis dizer “tamanho-1”. */
Lower[pivo][pivo] = 1.0;
for( linha = 0; linha < tamanho - 1; linha++ )
for( coluna = tamanho - 1; coluna > linha; coluna-- ) /* Possivelmente você quis dizer “tamanho-2”. */
Lower[linha][coluna] = 0.0;
printf( "\n\n\tMatriz L: " ); ExibirMatriz( Lower, tamanho, tamanho - 1 );

printf( "\n\n\tMatriz U: " ); ExibirMatriz( matriz, tamanho, tamanho - 1 );
//A EXECUÇÃO FOI ATÉ AQUI, EM VERDADE, EXIBIU APENAS PARTE DA MATRIZ E RETORNOU O ERRO.

/* Creio que deu para ver por quê. */
}


Com o tipo "double", simples, o erro não acontece com esse código e a fatoração é feita corretamente (testei-a já dezenas de vezes!).


Trocar o tipo do dado, nesse caso, não seria suficiente para corrigir o programa. Se isso de fato ajudou, foi por azar de o erro não afetar outros dados de um modo que você tenha conseguido perceber.

Outra coisa que eu notei é que você repete o código de desalocação várias vezes, em momentos que percebe condições de erro. Isso é enfadonho e sujeito a erro. Por que você não usa a forma usual de tratar tais situações em C? Tal forma se parece com o seguinte.

FloatType *LU_Factorization(FloatType **M, size_t n){
FloatType **L=NULL, *X=NULL;
if(!(L=calloc(n, sizeof *L)) || !(X=malloc(n*sizeof *X))
goto error;
for(size_t l=0; l<n; l++)
if(!(L[l]=malloc(n*sizeof *L[l]))
goto error;

/* ... seu código aqui ... */
if(situacao_de_erro_1)
goto error;
/* ... seu código aqui ... */
if(situacao_de_erro_2)
goto error;
/* ... seu código aqui ... */
if(situacao_de_erro_3)
goto error;
/* ... seu código aqui ... */

/* Se chegou neste ponto, executou sem erro, retorna o valor de X. */
goto common_exit; // Pula o código de tratamento de erro.

error:
/* Em caso de erro, desaloca X e o faz novamente nulo. */
free(X);
X=NULL;

common_exit:
/* Esta desalocação tem ser feita sempre, com ou sem erro. */
for(size_t l=n; l>0;)
free(L[--l])
free(L);

return X;
}


(Se alguém disser que o código é feio porque tem goto, é porque tal palpiteiro não sabe C. Mande-o estudar.)


... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)


10. Re: Tipo long double

José
DeuRuimDotCom

(usa Linux Mint)

Enviado em 10/09/2019 - 00:41h

Paulo, o senhor se importaria em me dizer quanto tempo de janela é preciso para ter um olho arguto desse seu? rsrsrs
Na verdade, esse erro que detectou se deveu a uma alteração na função que fiz. Seu protótipo inicial era o seguinte:

double* FatoracaoLU( double** matriz, unsigned int numero_equacoes, unsigned int dimensao, FILE* arquivoSaida )
...
for( pivo = 0; pivo < dimensao - 1; pivo++ ) /* Loop original */
Lower[pivo][pivo] = 1.0;
...

Reduzi o numero de parâmetros porque inúteis dois inteiros nesse caso (mas úteis em outros métodos que tenho implementado e em que me baseara aqui), já que o método fatLU exige matrizes não singulares, e acabei me equivocando.
Todavia, mesmo com esse erro, compilado pelo Dev, CodeBlocks ou diretamente pela linha de comando, funcionou a operação com "double", porém, não com o "long double".
Mas, de fato, o retorno "free(): invalid pointer Abortado (imagem do núcleo gravada)", que ocorre com "long double", deveu-se a isso mesmo.

paulo1205 escreveu:
Outra coisa que eu notei é que você repete o código de desalocação várias vezes, em momentos que percebe condições de erro. Isso é enfadonho e sujeito a erro. Por que você não usa a forma usual de tratar tais situações em C?

Na verdade, porque não sabia a forma usual!!! Já atualizei o código para o modo que ensinou, muito mais limpo mesmo.
Sobre o "goto", é fato isso que o senhor diz, de que desde o "Hello, World!" professores etc. recomendam ignorá-lo. De uns tempos pra cá que comecei a cogitar sobre sua utilidade, mas meu automático ainda não aprendeu a trabalhar com ele.