Arquiteturas exóticas com as quais os comitês de padrões se preocupam

154

Eu sei que os padrões C e C ++ deixam muitos aspectos da implementação de linguagem definidos apenas porque, se houver uma arquitetura com outras características, seria muito difícil ou impossível escrever um compilador em conformidade com ele.

Eu sei que 40 anos atrás, qualquer computador tinha sua própria especificação. No entanto, não conheço nenhuma arquitetura usada hoje em que:

  • CHAR_BIT != 8
  • signed não é o complemento de dois (ouvi dizer que Java teve problemas com este).
  • O ponto flutuante não é compatível com IEEE 754 (Editar: eu quis dizer "não está na codificação binária IEEE 754").

A razão pela qual estou perguntando é que muitas vezes explico às pessoas que é bom que o C ++ não exija outros aspectos de baixo nível, como tipos de tamanho fixo . É bom porque, diferentemente de outras línguas, torna seu código portátil quando usado corretamente (Edit: porque ele pode ser portado para mais arquiteturas sem exigir a emulação de aspectos de baixo nível da máquina, como por exemplo, aritmética de complemento de dois na arquitetura sign + magnitude) . Mas me sinto mal por não poder apontar para nenhuma arquitetura específica.

Portanto, a pergunta é: quais arquiteturas exibem as propriedades acima?

uint*_ts são opcionais.

Yakov Galka
fonte
9
Eu acho que você tem ao contrário. Se o C ++ exigisse, digamos, dois complementos para números inteiros assinados, tornaria o código C ++ mais portátil e não menos. A questão de por que o comitê de padrões C ++ não determina isso é outra questão. Especialmente porque, apesar do que você diz, não seria impossível escrever um compilador para uma arquitetura não-padrão, você sempre pode simular caracteres de 8 bits ou dois complementos aritméticos, mesmo quando sua plataforma não o suporta diretamente.
Joao
8
@ john: seria impraticável, de modo que o compilador em conformidade não-padrão gerasse um código mais rápido do que um em conformidade. E ainda não vejo como isso torna seu código mais portátil.
Yakov Galka
4
Tenho certeza de que a verdadeira razão para o padrão ser assim não é porque é uma solução ideal. Porém, é porque quando o padrão foi escrito, muitos compiladores C e C ++ já existiam, e o comitê de padrões não queria rejeitar os compiladores existentes.
john
4
@ john: Eu duvido que "facilitar a criação de escritores de compiladores" seja uma prioridade ao criar o padrão C ++ (eles estariam fazendo um trabalho horrível, se fosse, pois o C ++ é uma das linguagens mais difíceis de analisar e outros aspectos de o idioma não facilita exatamente para os escritores do compilador). Desempenho, amplo suporte à plataforma e compatibilidade com versões anteriores são bastante importantes. E todos esses três sofreriam se as restrições mencionadas fossem adicionadas ao padrão.
Sander De Dycker
5
Não se trata do compilador, mas do hardware. O C ++ deixa algumas coisas não especificadas para permitir o uso direto dos recursos de hardware. Seus aplicativos de telefone não serão executados em um mainframe de qualquer maneira, portanto, não há portabilidade, no entanto, conforme o código.
Bo Persson

Respostas:

114

Dê uma olhada neste

Servidores Unisys ClearPath Dorado

oferecendo compatibilidade com versões anteriores para pessoas que ainda não migraram todos os seus softwares Univac.

Pontos chave:

  • Palavras de 36 bits
  • CHAR_BIT == 9
  • complemento de alguém
  • Ponto flutuante não-IEEE de 72 bits
  • espaço de endereço separado para código e dados
  • endereçado por palavra
  • nenhum ponteiro de pilha dedicado

Não sei se eles oferecem um compilador C ++, mas poderiam .


E agora surgiu um link para uma edição recente do manual C:

Manual de referência de programação do compilador Unisys C

A Seção 4.5 possui uma tabela de tipos de dados com 9, 18, 36 e 72 bits.

tamanho e intervalo de tipos de dados no compilador USC C

Bo Persson
fonte
13
Eu acho que void * deve ser infernal para usar nessa arquitetura.
luiscubal
13
@ybungalobill - Eu acredito char*e void*deve ser do mesmo tamanho, e grande o suficiente para segurar qualquer outro ponteiro. O resto depende da implementação.
Bo Persson
22
@ybungalobill: Nos antigos compiladores Win16, os ponteiros regulares estavam próximos dos ponteiros e continham apenas um deslocamento de 16 bits; portanto sizeof(int*) == 2, os ponteiros distantes também tinham um seletor de 16 bits sizeof(void*) == 4.
Adam Rosenfield
10
Existe, ou costumava haver, um manual on-line para seu compilador C ++. Também vale ressaltar que essa é apenas uma das arquiteturas de mainframe da Unisys: a outra é uma arquitetura de magnitude assinada de 48 bits (para a qual encontrei apenas um manual C, não um C ++). Com relação ao resto: não acho isso sizeof(int*) != sizeof(char*)aqui: ambos são 36 bits. Mas o seletor de bytes no char*está nos bits de ordem superior e é ignorado int*. (Eu usei outras máquinas, no entanto, em que `sizeof (char *)> sizeof (int *)).
James Kanze
16
@ Adam Rosenfield Nos compiladores de 16 bits do MS / DOS, você tinha "modos" diferentes e os ponteiros de dados não eram necessariamente do mesmo tamanho que os ponteiros de função. Mas, pelo menos nos que eu usei, todos os ponteiros de dados (inclusive void*) sempre tiveram o mesmo tamanho. (Claro, você não pode converter um ponteiro de função para void*, uma vez que void*pode ser menor, mas de acordo com o padrão, você não pode fazer isso hoje, qualquer um..)
James Kanze
51

Nenhuma de suas suposições vale para mainframes. Para iniciantes, não conheço um mainframe que use o IEEE 754: a IBM usa o ponto flutuante da base 16 e os dois mainframes da Unisys usam a base 8. As máquinas Unisys são um pouco especiais em muitos outros aspectos: Bo mencionou o 2200 arquitetura, mas a arquitetura MPS é ainda mais estranha: palavras marcadas com 48 bits. (Se a palavra é um ponteiro ou não, depende de um pouco na palavra.) E as representações numéricas são projetadas para que não haja distinção real entre ponto flutuante e aritmética integral: o ponto flutuante é a base 8; ele não requer normalização e, ao contrário de todos os outros pontos flutuantes que eu vi, coloca o decimal à direita da mantissa, em vez da esquerda, e usa magnitude assinada para o expoente (além da mantissa). Com os resultados que um valor integral de ponto flutuante possui (ou pode ter) exatamente a mesma representação de bits que um número inteiro de magnitude assinado. E não há instruções aritméticas de ponto flutuante: se os expoentes dos dois valores são ambos 0, a instrução faz aritmética integral, caso contrário, faz aritmética de ponto flutuante. (Uma continuação da filosofia de marcação na arquitetura.) O que significa que, emboraint pode ocupar 48 bits, 8 deles devem ser 0 ou o valor não será tratado como um número inteiro.

James Kanze
fonte
4
Os mainframes IBM (z / Architecture) suportam o ponto flutuante IEE754.
Nikita Nemkin
1
fyi veja este comentário no twitter
Shafik Yaghmour 14/11
6
@ Nikita - Eles fazem agora . Inicialmente, era um complemento (caro) para suportar Java.
Bo Persson
42

A conformidade total com o IEEE 754 é rara em implementações de ponto flutuante. E enfraquecer a especificação a esse respeito permite muitas otimizações.

Por exemplo, o suporte subnorm difere entre x87 e SSE.

Otimizações como a fusão de uma multiplicação e adição que foram separadas no código-fonte também alteram levemente os resultados, mas é uma ótima otimização em algumas arquiteturas.

Ou no x86, a conformidade estrita com a IEEE pode exigir a definição de determinados sinalizadores ou transferências adicionais entre registradores de ponto flutuante e memória normal para forçá-lo a usar o tipo de ponto flutuante especificado em vez de flutuações internas de 80 bits.

E algumas plataformas não possuem flutuadores de hardware e, portanto, precisam imitá-las no software. E alguns dos requisitos do IEEE 754 podem ser caros de implementar em software. Em particular, as regras de arredondamento podem ser um problema.

Minha conclusão é que você não precisa de arquiteturas exóticas para entrar em situações em que nem sempre deseja garantir uma conformidade estrita com o IEEE. Por esse motivo, poucas linguagens de programação garantem conformidade estrita com o IEEE.

CodesInChaos
fonte
7
Outro conjunto "exótico" de hardware são os mainframes da IBM, nos quais o formato de ponto flutuante é anterior ao padrão IEEE. Ao contrário do Java, o C ++ ainda pode usar o hardware existente.
Bo Persson
5
O IEEE 754 não é totalmente suportado por GPUs.
kerem
3
A falta de conformidade estrita com o IEEE 754 é um incômodo para alguns, mas não acho que esteja exatamente no escopo dos problemas com os quais o OP realmente se importa.
omniforme
3
@ Matthieu Como também está marcado como "C", devo mencionar um analisador C que pode informar todos os valores que seu programa de ponto flutuante pode assumir com registros de ponto flutuante de 80 bits derramados na memória por capricho do compilador C. blog.frama-c.com/index.php?post/2011/03/03/cosine-for-real
Pascal Cuoq
2
@ MatthieuM .: É uma pena que o ISO / ANSI não permita que parâmetros variados especifiquem tamanhos mínimos / máximos para argumentos de ponto flutuante e número inteiro; se tivessem, os 80 bits long doublepoderiam ter sido do tipo útil e duradouro, já que o único problema real era que ele trabalha mal printf. O fato de que o duplo estendido armazena o líder 1 acelera explicitamente os cálculos em sistemas que não são da FPU e também elimina a necessidade de tratamento especial de denormals em qualquer contexto que não seja a conversão de / para outros tipos. Pena que o C printfestragou tudo.
Supercat
40

Eu encontrei este link listando alguns sistemas onde CHAR_BIT != 8. Eles incluem

alguns DSPs de TI têm CHAR_BIT == 16

Chip BlueCore-5 (um chip Bluetooth da Cambridge Silicon Radio) que possui CHAR_BIT == 16.

E, claro, há uma pergunta sobre o Stack Overflow: Quais plataformas têm algo diferente de char de 8 bits

Quanto aos sistemas sem complemento de dois, há uma leitura interessante sobre o comp.lang.c ++. Moderado . Resumido: existem plataformas que possuem complemento ou sinal e representação de magnitude.

dcn
fonte
5
Dispositivos analógicos O SHARC DSP de 32 bits possui CHAR_BIT=32e o Texas Instruments DSP do TMS32F28xx possui CHAR_BIT=16. O GCC 3.2 para PDP-10 possui CHAR_BIT=9. Eu acho que o S / 360 também pode ter um char não de 8 bits.
Osgx
1
Eu ainda gostaria de um exemplo para arquiteturas de 'complemento de dois não'. Especialmente porque aconteceu que CHAR_BITSé uma duplicata parcial.
Yakov Galka
Os DSPs da TI têm caracteres de 16 bits apenas porque os implementadores o escolheram (seria um pouco mais trabalhoso fazê-lo funcionar corretamente, mas não um IIRC absurdamente difícil - provavelmente apenas alguns "buracos" no andaime de codegen no compilador subjacente) . Portanto, não é um motivo arquitetônico profundo. O código C funciona em uma máquina abstrata. Se tudo o que você tem são INTs de 16 bits, armazene dois caracteres em cada um e adicione mesclagem de leitura, modificação e gravação ao otimizador de olho mágico (no mínimo). Claro, é mais trabalho, mas veja quanto mais trabalho é para todos lidar com tipos tão estranhos em lugares onde eles nunca aparecerão. Que nojo.
Restabelecer Monica
24

Tenho certeza de que os sistemas VAX ainda estão em uso. Eles não suportam ponto flutuante IEEE; eles usam seus próprios formatos. O Alpha suporta os formatos de ponto flutuante VAX e IEEE.

Máquinas de vetores Cray, como o T90, também têm seu próprio formato de ponto flutuante, embora os sistemas Cray mais novos usem IEEE. (O T90 que eu usei foi desativado há alguns anos; não sei se ainda há algum em uso ativo.)

O T90 também tinha / tem algumas representações interessantes para ponteiros e números inteiros. Um endereço nativo pode apontar apenas para uma palavra de 64 bits. Os compiladores C e C ++ tinham CHAR_BIT == 8 (necessário porque ele executava o Unicos, uma versão do Unix, e precisava interoperar com outros sistemas), mas um endereço nativo só poderia apontar para uma palavra de 64 bits. Todas as operações no nível de bytes foram sintetizadas pelo compilador e a void*ou char*armazenou um deslocamento de bytes nos 3 bits de ordem superior da palavra. E acho que alguns tipos inteiros tinham bits de preenchimento.

Os mainframes da IBM são outro exemplo.

Por outro lado, esses sistemas específicos não precisam necessariamente impedir alterações no padrão de linguagem. Cray não mostrou nenhum interesse particular em atualizar seu compilador C para C99; presumivelmente a mesma coisa aplicada ao compilador C ++. ele pode ser razoável para apertar os requisitos para implementações hospedadas, como a exigência CHAR_BIT == 8, formato IEEE de ponto flutuante se não a semântica completa, e 2 'S-complemento sem padding bits para inteiros assinados. Os sistemas antigos poderiam continuar a oferecer suporte aos padrões anteriores de linguagem (o C90 não morreu quando o C99 foi lançado) e os requisitos podem ser mais flexíveis para implementações independentes (sistemas embarcados), como DSPs.

Por outro lado, pode haver boas razões para sistemas futuros fazerem coisas que seriam consideradas exóticas hoje.

Keith Thompson
fonte
6
Um bom argumento no final sobre o quão rigorosos padrões impedem a inovação. Quando obtivermos computadores quânticos (ou orgânicos) com estados trinários, os requisitos de módulo aritmético para unsignedtipos integrais serão uma grande dor, enquanto que a aritmética assinada ficará bem.
Ben Voigt
@BenVoigt Por que essa aritmética não assinada é uma dor? Não é possível adicionar módulos 3 ^ n nesses computadores?
phuclv
2
@ LưuVĩnhPhúc: Esse é exatamente o ponto, com as operações de hardware realizadas no módulo 3 ** n, fornecendo tipos não assinados em C ++ cujas operações são definidas no módulo 2 ** n será difícil.
Ben Voigt
2
Conheço um VAX 11/780 ainda em uso como host para um compilador cruzado visando um sistema embarcado especializado com uma arquitetura proprietária. Para sustentar esse VAX em particular, os guardiões se aproximaram dos museus em busca de peças de reposição.
Peter
2
@ Keith - tecnicamente, o único obstáculo é passar por um processo para fornecer evidências que atendam aos requisitos regulatórios, já que o sistema embarcado de destino é de alta criticidade. Existem muitos obstáculos não técnicos (política organizacional, etc.), que até o momento foram intransponíveis. Atualmente, é mais fácil montar um caso para invadir museus do que atualizar o host.
Pedro #
16

CHAR_BITS

De acordo com o código fonte do gcc :

CHAR_BITé 16bits para 1750a , dsp16xx arquiteturas.
CHAR_BITé 24bits para a arquitetura dsp56k .
CHAR_BITé 32bits para a arquitetura c4x .

Você pode encontrar mais facilmente fazendo:

find $GCC_SOURCE_TREE -type f | xargs grep "#define CHAR_TYPE_SIZE"

ou

find $GCC_SOURCE_TREE -type f | xargs grep "#define BITS_PER_UNIT"

se CHAR_TYPE_SIZEestiver definido adequadamente.

Conformidade com IEEE 754

Se a arquitetura de destino não suportar instruções de ponto flutuante, o gcc poderá gerar fallback de software, que por padrão não é compatível com o padrão. -funsafe-math-optimizationsAlém disso , opções especiais (como a bruxa também desativa a preservação de zeros) podem ser usadas.

ivaigult
fonte
3
votado por simplesmente direcionar o OP para procurar a fonte de um compilador popular; essa é a definição de RFTM nesse caso, portanto, deve ser o primeiro local em que as pessoas olham.
Underscore_d
9

A representação binária IEEE 754 era incomum em GPUs até recentemente, consulte Paranoia de ponto flutuante da GPU .

EDIT: uma questão foi levantada nos comentários se o ponto flutuante da GPU é relevante para a programação de computador usual, não relacionada aos gráficos. Claro que sim! A maior parte do desempenho industrial atualmente calculado é feito em GPUs; a lista inclui IA, mineração de dados, redes neurais, simulações físicas, previsão do tempo e muito mais. Um dos links nos comentários mostra o motivo: uma vantagem de ponto flutuante de ordem de magnitude das GPUs.

Outra coisa que eu gostaria de acrescentar, que é mais relevante para a questão do OP: o que as pessoas fizeram 10 a 15 anos atrás quando o ponto flutuante da GPU não era o IEEE e quando não havia API como o OpenCL ou CUDA de hoje para programar GPUs? Acredite ou não, os pioneiros da computação em GPU conseguiram programar GPUs sem uma API para fazer isso ! Eu conheci um deles na minha empresa. Aqui está o que ele fez: ele codificou os dados que precisava para calcular como uma imagem com pixels representando os valores em que estava trabalhando, depois usou o OpenGL para executar as operações necessárias (como "desfoque gaussiano" para representar uma convolução com uma distribuição normal) , etc) e decodificou a imagem resultante novamente em uma matriz de resultados. E isso ainda era mais rápido do que usar CPU!

Coisas assim foram o que levou a NVidia a finalmente tornar seus dados internos binários compatíveis com o IEEE e a introduzir uma API orientada à computação, e não à manipulação de imagens.

Michael
fonte
Como as GPUs são relevantes? (a) Esta página parece muito desatualizada. (b) Até hoje, você não pode programar GPUs em C: porque C suporta coisas como funções recursivas que as GPUs, até onde eu sei, não. Portanto, você não pode nem escrever um compilador, se quiser.
Yakov Galka
1
@ybungalobill, transferir o trabalho repetitivo para a GPU é atualmente o método preferido para cálculos em larga escala . De fato, atualmente estou desenvolvendo um em C ++. Felizmente, trabalhamos apenas com as GPUs NVidia CUDA que possuem representação binária compatível com IEEE 754.
Michael
Não digo que as GPUs não sejam usadas para cálculos de GP. Eu disse que você realmente não programa os kernels em C, apesar da semelhança de sintaxe. Você pode executar int f(int n) { return n <= 1 ? 1 : n * f(n-1); }no CUDA? Se não, as GPUs não são relevantes para esta pergunta (que pergunta sobre os comitês C e C ++).
Yakov Galka
6
@ybungalobill: várias respostas para isso. Primeiro, o CUDA suporta C, C ++ e Fortran . Veja o mesmo link para obter a enorme vantagem de desempenho das GPUs de 2048 threads em relação à sua CPU de 8 threads típica. Segundo, é verdade que apenas subconjuntos (embora grandes) dessas linguagens são suportados, incluindo a falta de suporte para a recursão do modelo de programação CUDA apropriada (chamada "paralelismo dinâmico") até CUDA 5.0. Terceiro, as recursões geralmente podem ser substituídas por loops, necessários para o desempenho multithread de qualquer maneira.
Michael