sábado, 28 de abril de 2007

Numéricos inteiros - Delphi

Existem vários tipos diferentes para dados numéricos no Delphi. Os tipos mais comumente usados são o “Integer” e o “Real”, eles podem parecer suficientes e satisfatórios para seu cotidiano até o dia ... que ao candidatar-se para uma vaga ... aquela provinha básica ... surge uma questão do tipo:

a) function ProcessaNumero(value: Byte): integer; overload;
b) function ProcessaNumero(value: ShortInt): integer; overload;
c) function ProcessaNumero(value: word): integer; overload;
d) function ProcessaNumero(value: Cardinal): integer; overload;
e) function ProcessaNumero(value: Double): integer; overload;

o trecho de código abaixo irá executar qual das versões sobrecarregadas da função “ProcessaNumero”?

var
ZnNumber: Integer;
begin
ZnNumber := ProcessaNumero(-1);
end;


Na maioria das linguagens de programação vc vai ter tipos numéricos inteiros de 8 , 16, 32 e 64 bits, no Delphi não é diferente. O chamado grupo dos tipos “Integer types” apresentam algumas características que devem ser observadas. No que tange a sinalização, exceto o de 64 bits, esses grupos se dividem em sinalizados e não sinalizados. Ou seja, tipos numéricos cujo tamanho reservado para ele será de 8 bits, sinalizado (positivo e negativo) e não sinalizado (apenas positivos). O mesmo para os de 16, bem como para os de 32 bits. Esses tipos, que se enquadram dentro desse contexto, sinalizados e não, quem são eles?

8 bits:
Byte – Não sinalizado.
Shortint – Sinalizado.

16 bits:
Word - Não sinalizado.
Smallint – Sinalizado.

32 bits:
Longword - Não sinalizado.
Longint - Sinalizado.


Vc pode me perguntar: E o Interger? E o Cardinal? Não estariam dentro dessa situação?
Antes de responder, gostaria que vc me respondesse: Qual é o tamanho do Integer? 32 bits, seria a sua resposta? Qual o tamanho do Cardinal? 32 bits, seria a sua resposta? Pois bem amigo, o Integer, bem como o Cardinal, variam de tamanho de acordo com a versão do Delphi. Em outras palavras, se Delphi 16 bits, então Integer e Cardinal possuem tamanho de 16 bits, o mesmo vale para o Delphi 32 bits. Isso acontece para compatibilidade com arquitetura do processador e o sistema operacional. Portanto, não me surpreenderia se o Integer, a partir do Delphi 64 bits, que virá em breve, novamente tenha seu tamanho adaptado a nova arquitetura. Portanto, o Integer e o LongInt não são exatamente a mesma coisa. Do mesmo modo o Cardinal e o Longword, são a partir desse ponto de vista, são tipos distintos. Observe que o Integer e Cardinal são usados com freqüência pq eles correspondem a representação numérica nativa da CPU.
No mais, como recurso mnemônico para distinguir quais são os sinalizados e quais não são, observe como referencia a partícula “int” no nome do tipo. Todos os sinalizados possuem “int” no nome. Exemplo:

Sinalizados: Shortint, smallin, longint e Integer.
Não sinalizados: Byte, Word, Longword, Cardinal.

Generic integer types










Type Range Format .NET Type Mapping
Integer -2147483648..2147483647 signed 32-bit System.Int32
Cardinal 0..4294967295 unsigned 32-bit System.UInt32


Fundamental integer types include Shortint, Smallint, Longint, Int64, Byte, Word, and Longword.

Fundamental integer types


















































Type

Range

Format

.NET Type Mapping

Shortint

-128..127

signed 8-bit

System.SByte

Smallint

-32768..32767

signed 16-bit

System.Int16

Longint

-2147483648..2147483647

signed 32-bit

System.Int32

Int64

-2^63..2^63-1

signed 64-bit

System.Int64

Byte

0..255

unsigned 8-bit

System.Byte

Word

0..65535

unsigned 16-bit

System.UInt16

Longword

0..4294967295

unsigned 32-bit

System.UInt32



Em geral operações aritméticas com inteiros retorna um valor do tipo Integer, 32 bits.
Um retorno de 64 bits apenas é possível quando um ou mais operadores sejam Int64
A seguir, um exemplo de código que produzirá um resultado incorreto.


function ResultadoIncorreto(Value: Integer): Int64;
var
Aux: Integer;
Begin
(* Atribuindo o máximo valor de um numérico sinalizado de 32 bits *)
Aux := High(Value);
(* ao somar mais um, estou ultrapassando a faixa de tamanho para esse tipo.
Ou seja, supostamente estou forçando um resultado cujo o tipo seja de
64 bits *)
result := Aux + 1; // contudo, o resultado não é o esperado ... hehhehehhe
end;


procedure TForm1.Button1Click(Sender: TObject);
const
Msg = 'Resultado %d';
begin
self.Caption := Format(Msg, [ResultadoIncorreto(200)]);
end;


A forma correta para se chegar ao resultado esperado seria utilizando um cast para 64 bits:

function ResultadoIncorreto(Value: Integer): Int64;
var
Aux: Integer;
begin
(* Atribuindo o máximo valor de um numérico sinalizado de 32 bits *)
Aux := High(Value);
(* ao somar mais um, estou ultrapassando a faixa de tamanho para esse tipo.
Contudo, para que meu resultado seja capaz de suportar este número preciso
fazer um cast para que resultado da operação seja de 64 bits *)
result := Int64(Aux) + 1; // Agora sim conseguirei o resultado esperado.
end;


Obs:
As funções suportam plenamente como argumentos valores Int64. Todavia, algumas outras funções padrões truncarão argumentos desse tamanho. Veja, High, Low, Succ, Pred, Inc, Dec, IntToStr, e IntToHexRound, Trunc, StrToInt64, e StrToInt64Def são funções que retornam valores Int64.


Outro exemplo:

O que acontece quando vc tenta incrementar o último valor de um tipo inteiro? Ou quando vc tenta decrementar o primeiro? No caso de incrementar o último, vai retornar o primeiro valor. Como vc pode observar no exemplo anterior quando não fizemos o cast para Int64, o retorno foi “-2147483648”. No outro caso, justamente o contrário. Se eu tentar decrementar -2147483648 o retorno será “2147483648” positivo. Isso acontece para todos os “Integer Types”. Teste o trecho de código abaixo:

procedure TForm1.Button1Click(Sender: TObject);
var
I: Shortint;
begin
I := High(Shortint);
I := I + 1;
Self.Caption := Format('%d', [i])
end;

O resultado será – 128. Contudo, se nas opções do projeto (menu Project ► Options), em compiler a checkbox range-checking estiver checada, habilitando a checagem de range, vc vai receber um exceção bacana.







Agora pq isso acontece? Eis o “x” da questão ... vc vai decorar que isso acontece com os Integer Types? Garanto que se vc entender o pq não vai precisar decorar.


Ordinal Types

A Delphi language classifica os tipos de acordo com algumas características que eles apresentam segundo sua estrutura. Existem os Simple Types, um sub-conjunto deles são os Ordinal Types. Dele fazem parte os integer, character, Boolean, enumerated, e subrange types.
Um Ordinal Type, define um conjunto de valores no qual cada valor, exceto o primeiro, possui obrigatoriamente um único predecessor. Bem como, cada valor, exceto o último possui um único sucessor.
É importante conhecer as funções predefinadas que operam sobre os ordinal types.






































Function

Parameter

Return value

Remarks

Ord

ordinal expression

ordinality of expression's value

Does not take Int64 arguments.

Pred

ordinal expression

predecessor of expression's value


Succ

ordinal expression

successor of expression's value


High

ordinal type identifier or variable of ordinal type

highest value in type

Also operates on short-string types and arrays.

Low

ordinal type identifier or variable of ordinal type

lowest value in type

Also operates on short-string types and arrays.


Exemplo:

A) High(Byte) retorna 255, pq o maior valor possível num tipo byte é 255.
B) Succ(5) retorna 6. Pq 5 é um tratado pelo compilador como um tipo Integer, logo, o inteiro sucessor de 5 é o 6.
C) No caso de booleanos é correto a expressão: Succ(False) = True;


As procedures Inc() e Dec(), incrementam e decrementam valores cujo o tipo pertença ao conjunto dos Ordinal Types. Por exemplo

Inc(I);// é equivalente a Succ(i), ou I := I + 1;






Nenhum comentário:

Postar um comentário