Proposta para C++: byte deve ter exatamente 8 bits
(open-std.org)- O P3477R0 é uma proposta de mudança no padrão para que o tamanho do byte em C++ não fique a cargo do valor definido pela implementação chamado
CHAR_BIT, mas seja fixado em exatamente 8 bits - As plataformas modernas já convergiram para o pressuposto de bytes de 8 bits, e GCC, LLVM e MSVC também definem os respectivos valores padrão ou macros como 8
- O POSIX exige
CHAR_BIT == 8desde o POSIX.1-2001, e a adoção de representação inteira em complemento de dois no C++20 e no C23 segue na mesma direção - O suporte a bytes que não têm 8 bits deixa pequenas exceções em toda a linguagem, biblioteca e toolchain, criando um ônus de caso de borda que não condiz com o uso moderno real de C++
- Arquiteturas excepcionais como PDP-10 ou alguns DSPs existem, mas a questão central é se o novo padrão C++ deve continuar mantendo essa complexidade para atendê-las
Objetivo de mudança do P3477R0
- C++ herdou de C a macro
CHAR_BIT, e atualmente esse valor é um valor definido pela implementação que indica o número de bits em um byte - O P3477R0 propõe alterar o padrão C++ para exigir oficialmente que um byte tenha 8 bits
- A avaliação por trás disso é que, no início da computação, a flexibilidade de permitir diversos tamanhos de byte fazia sentido, mas o hardware moderno quase todo convergiu para bytes de 8 bits
Estado atual dos compiladores e plataformas
- Os principais compiladores já tratam bytes de 8 bits como a realidade padrão
- Entre os casos antigos de suporte do GCC,
dsp16xxfoi removido em 2004, e1750aem 2002 - Buscas na web mostram alguns ports externos do GCC em que
BITS_PER_UNITnão é 8, mas a proposta considera que eles não parecem relevantes para C++ moderno
POSIX e a tendência na representação de inteiros
- O POSIX exige as seguintes condições desde o POSIX.1-2001
- Um byte tem exatamente 8 bits
CHAR_BITé8SCHAR_MAXé127,SCHAR_MINé-128,UCHAR_MAXé255
- O POSIX explica que, como resultado da adição de
int8_t, exigecharde 8 bits e aritmética em complemento de dois - Desde o P0907r4, o C++20 oferece suporte apenas ao formato de armazenamento em complemento de dois, e o C23 segue a mesma direção
- Como exemplos atuais de sistemas operacionais compatíveis com POSIX, são listados AIX, HP-UX, INTEGRITY, macOS, OpenServer, UnixWare, VxWorks e vz/OS
O custo deixado por bytes que não têm 8 bits
- Software para bytes de 8 bits e software para bytes que não têm 8 bits não são compatíveis entre si, e o código C/C++ voltado a bytes que não têm 8 bits é visto, na prática, como algo próximo de dialetos incompatíveis de C e C++
- O suporte a arquiteturas com bytes que não têm 8 bits deixa pequenas complexidades desnecessárias em várias partes da linguagem e da biblioteca
- Compiladores e toolchains precisam continuar carregando casos de borda que não refletem o uso moderno
- Novos programadores tendem a se confundir com essas características exóticas de C++
- Alguns programadores experientes acabam gastando tempo com “portabilidade” para plataformas que não existem
Arquiteturas de exceção e alternativa de compromisso
- A proposta também reconhece que processadores com bytes que não têm 8 bits ainda existem
- A pergunta central é se esses processadores são relevantes para o C++ moderno e se seus usuários usarão novas versões de C++
- Como alternativa de compromisso, também é apresentada a possibilidade de exigir
CHAR_BIT % 8 == 0, mas isso só faz sentido se o comitê decidir continuar dando suporte a DSPs ou outros processadores em queCHAR_BITnão é 8, mas é múltiplo de 8 - O PDP-10 é objeto da discussão, mas o PDP-11 é distinguido por usar bytes de 8 bits
- Alguns DSPs tratam palavras de 24 ou 32 bits como “bytes”, e essas arquiteturas faziam sentido em uma época em que os tamanhos de palavra variavam e o conceito de byte não era padronizado
Direção das mudanças no texto do padrão
- A ideia é alterar a definição de byte em
intro.memorypara declarar que o byte, a unidade básica de armazenamento do modelo de memória de C++, tem 8 bits - Em
climits, a proposta sugere alterar o texto para queCHAR_BITseja8 - Em
cstdint, como um byte passa a ter 8 bits, tipos inteiros com largura especificada comoint8_t,uint8_te macros relacionadas deixam de ser opcionais - Entre os tipos que usam
_N_, aqueles em queNnão é8,16,32ou64continuam opcionais - A proposta inclui a remoção de quatro cláusulas de mandates relacionadas a
CHAR_BIT == 8emlocalization
Relação com o padrão C
- A proposta examina se C++ deve continuar sendo relevante para arquiteturas com bytes que não têm 8 bits
- O comitê de C pode chegar a uma conclusão diferente para a linguagem C
- Embora o ideal seja que os dois comitês estejam alinhados, esta proposta deixa o grupo de contato WG14 e SG22 no papel de fornecer informações ao WG21
1 comentários
Opiniões no Hacker News
A série do JF “não dá para admitir que computadores reais já funcionam todos assim?” já teve um episódio sobre inteiros com sinal serem complemento de dois: "Signed Integers are Two’s Complement"
Só que isso poderia tornar obsoleto este código clássico do kernel Linux: https://github.com/torvalds/linux/blob/master/include/math-emu/double.h#L29
Em 1986, quando eu era estagiário, escrevi código C em um BBN C/70 que usava bytes de 10 bits; foi uma experiência horrível, e a própria existência desse tipo de máquina foi um acidente cósmico no sentido negativo
Ele usava palavras de 36 bits e bytes de 7 bits, e sobravam bits quando se empacotavam bytes em uma palavra. Ainda por cima me deram a tarefa de ler uma fita com dados binários em formato de 8 bits, então foi uma confusão
Mas C de 10 bits é outra história
D deu um grande passo à frente ao definir o seguinte: byte tem 8 bits, short tem 16 bits, int tem 32 bits, long tem 64 bits, a aritmética é complemento de dois, e ponto flutuante é ponto flutuante IEEE
Isso poupou uma quantidade enorme de tempo que era gasto tentando abstrair essas coisas e no fim fazendo errado, e milhões de pessoas respiraram aliviadas. O conjunto de caracteres também era Unicode, não EBCDIC nem RADIX-50
u8/i8,u16/i16,u32/i32,u64/i64, e a aritmética também é escolhida explicitamenteOverflow em
+é comportamento incorreto, então interrompe em debug e releasesafe;+%é wrapping em complemento de dois;+|é aritmética saturada.@addWithOverflow()fornece uma tupla do tipo original eu1, estd.math.add()retorna erro em overflow.f16,f32,f64,f80ef128também são, respectivamente, tipos de ponto flutuante IEEE com esses comprimentos em bits. Não importa quantos bits tem um byte; em uma máquina com bytes de 12 bits, bastaria usaru12ei12u8,i32, são muito melhores em todos os aspectosunsignederrado, mas acertou ao padronizar a quantidade de bits dos tipos primitivosbyte = 8 bits,short = 16,int = 32,long = 64,float = IEEE de 32 bits,double = IEEE de 64 bitsAinda há pessoas que precisam lidar com DSPs: https://thephd.dev/conformance-should-mean-something-fputc-and-freestanding#we-cannot-program-on--vibes-
Pessoalmente, estou documentando de brincadeira um console de fantasia de 12 bits não implementado, com “50% mais bits por byte que a concorrência!”, e incluí também invenções como “UTF-12”
Perguntei por anos, mas não recebi respostas positivas; o único mencionado foi algo da TI, então acrescentei a informação ao rascunho atualizado: https://isocpp.org/files/papers/D3477R1.html
CHAR_BIT=32em alguma versão como C++30, não vou protestar aos prantosFico me perguntando se C++ consegue descartar ou simplificar alguma coisa
É uma pergunta sincera, e não acompanhei em detalhes. Ouvi dizer que
rand()está quebrado e não dá para consertar, mas, até onde eu sabia, nem estava marcado para descontinuação. Esta proposta parece um teste do tipo “será que dá para remover também o suporte a uma solução para um problema que literalmente ninguém enfrenta?”C e C++ modernos abandonaram isso e exigem complemento de dois. Aqui, a distinção “as if” também não importa na prática, e dá para aplicar a mesma lógica a
CHAR_BIT, então claramente há precedente para esse tipo de mudançarandno caminho da descontinuação e ofereceram alternativasAlém disso, há
p2809 Trivial infinite loops are not Undefined Behavior,p1152 Deprecating volatile,p0907 Signed Integers are Two's Complement,p2723 Zero-initialize objects of automatic storage durationep2186 Removing Garbage Collection Support. Então é possível mudarAs especificações de exceção também foram removidas, embora haja quem queira ressuscitá-las para exceções por tipo de valor.
auto_ptrtambém foi removido por causa de um design quebrado. No entanto, em termos de simplificação, isso não melhora muito, porque ainda é preciso conhecer o jeito antigoSeria algo como precisar de um novo símbolo em C++ que indique de forma confiável um byte de 8 bits sem quebrar compatibilidade. Por exemplo, daria para criar
unsigned byte8,signed byte8em complemento de dois e atébyte8com comportamento de sinal não definido. Para contadores, haveriaunsigned decimal byte8esigned decimal byte8, com faixa de valores limitada a 0~10 e -10~+10; para contadores que também levam em conta o custo dos bytes,centimal byte8, de 0~100 e -100~+100; um tipo “mais ou menos suficiente” para o campoagede um banco de dados; e, obviamente, tambémfloat byte8. É uma piadarand()estaria quebrado. Ele gera valores que parecem aleatórios, e esse é o objetivoÉ claro que ele não gera números aleatórios criptograficamente seguros, e o mesmo vale para funções equivalentes em outras linguagens. Se a ideia é obter rapidamente inteiros razoavelmente aleatórios,
rand()funciona bem o suficienteObrigado pelo interesse na proposta; com base no feedback recebido até agora, fiz um rascunho atualizado: https://isocpp.org/files/papers/D3477R1.html
Em especial, a frase “a questão não é se ainda existem arquiteturas em que um byte não tem 8 bits. Existem! A questão é se elas se importam com C++ moderno, e se C++ moderno se importa com elas” é marcante
Tenho sentimentos ambivalentes sobre esta proposta. Por um lado, ela está obviamente certa, e não há uso significativo para
CHAR_BITque não seja 8Por outro lado, também parece ceder a uma visão de mundo justa segundo a qual o mundo deveria fazer sentido e ser passível de raciocínio apenas com base em um modelo pessoal e excessivamente simplificado do interior de um computador. Essa abordagem leva você bem longe, mas acaba em um beco sem saída; no fim, é preciso admitir que não se sabe nada, e que o melhor argumento formal é dizer que você construiu um programa correto apenas sob a condição de que a documentação esteja correta. Esse é um grande salto conceitual e, pessoalmente, quanto mais tempo passei sem ser forçado a admiti-lo, mais difícil foi superar isso depois. Ainda assim, projetos de eletrônica física parecem estar ficando populares entre iniciantes hoje em dia, então espero que o novo padrão não seja “leia a documentação”, mas “leia a maldita folha de dados”
autoconf, vejo eles verificarem quantos bits há em um byte e salvarem isso emconfig.h, como se alguém realmente planejasse agir com base nesse valorÉ preciso haver uma forma de verificar todo o código antigo durante a compilação para saber se esse macro já está sendo usado. Uma mudança incompatível desse tipo também corre o risco de dividir a linguagem. Também não está claro quão difícil seria testar se uma base de código existente usa o macro
CHAR_BITe se pode ser atualizada para um compilador novo. Surgem ainda dúvidas como quais bibliotecas seriam consideradas quebradas, ou se haveria problemas ao interagir com outro código compilado usandoCHAR_BIT. Concordo que é contraintuitivo, mas seria melhor primeiro criar uma ferramenta de conversão, mostrar que ela é segura mesmo em casos extremos e só então fazer a transiçãoGosto porque é uma proposta sem polêmica e, ao mesmo tempo, extremamente picante
Não vejo problema algum em impor
int8_t == char == 8 bits, mas não tenho tanta certeza sobre espalhar o mal-entendido de que um byte tem 8 bitsUm byte de 8 bits é chamado de octeto (octet). Ao mesmo tempo, desde C++17,
bytejá é uma espécie de “alias” dechar: https://en.cppreference.com/w/cpp/types/byteNos 45 anos seguintes, nunca vi “byte” ser usado com outro significado; então, se existe uma definição de “byte” que não seja 8 bits, é preciso uma fonte
Isso não é uma afirmação descritiva, é uma afirmação normativa
int8 == signed charstd::cout << (int8_t)32 << std::endl;obviamente deveria imprimir 32Não tem relação com C++, mas gosto bastante da ideia de um micromputador retrô com bytes de 6 bits. Algo em que 24 bits seriam uma word
Micromputadores normalmente lidam com um número pequeno de objetos e preferem arrays a ponteiros, então isso poderia economizar memória. VGA usava 6 bits por cor, dá para criar um alfabeto legível com uma matriz de bits 6x4, linguagens básicas como LISP ou Forth também caberiam em um alfabeto de 6 bits, e o System/360 original usava apenas endereços de 24 bits. Uma memória de 12 MiB em unidades de 6 bits endereçáveis de forma independente deveria ser suficiente para qualquer um. Se não bastar, dá para estender naturalmente o FAT-12 para FAT-24, ou usar ponteiros de 48 bits, tão úteis quanto ponteiros de 64 bits