2

Беззнаковые целые и Java

Недавно напоролся на один подводный камень в Java, который я до поры до времени обходил стороной потому, что не было необходимости собирать из байтов более длинные целые числа, а следовательно - и необходимости использовать поразрядные операции (сдвиг и т.п). Задача была простая - через порт от внешнего устр-ва приходит последовательность байт. Байты - это данные от 10-разрядного АЦП, первый байт - младший, второй - старший, третий - младший, четвертый - старший и т.д. Сделал я все как обычно, то есть наподобие того, как в этом примере:
import java.lang.*;

public class sign
{
public static void main(String [] args)
{
byte bytes[] = new byte[2];

bytes[0] = 0x56;//младший байт
bytes[1] = 0x02;//старший байт
short a = 0;
a = bytes[0];
a |= ((short)bytes[1]) << 8;
System.out.println("Value 1 = " + String.format("0x%04X", a) + "\r\n");
bytes[0] = (byte)0x85;//без оператора приведения не компилится
bytes[1] = 0x01;
a = bytes[0];
a |= ((short)bytes[1]) << 8;
System.out.println("Value 2 = " + String.format("0x%04X", a) + "\r\n");
}
}

Первый println выводит то, что и ожидалось - 0x256. Но вот второй... Сразу честно скажу: компилятор отказывался глотать этот пример, говоря, мол "possible loss of precision". Но в реальной ситуации байты шли из последовательного порта и предугадать их значения он в принципе не мог. И получалось у меня нечто невразумительное - примерно то, что выдавалось при печати во втором случае - вместо ожидаемого 0x0185 получилось 0xFF85. До сегодняшнего дня я думал, что беззнаковые числа нафиг не нужны и от них больше вреда, чем пользы, и авторы Java правильно сделали, что от них отказались. Но вот млин, были бы в они в Java. А еще лучше - наличие объединений (как в сях) или записей с вариантами (как в Паскале). Тогда бы вообще никаких сдвигов не надо, писваиваешь значения нужным полям, и... вуаля! Ну а по причине отсутствия в Java таких вкусностей, как объединения, проблему я решил следующим образом:
class sign2
{
public static void main(String [] args)
{
byte bytes[] = new byte[2];
short a;
bytes[0] = (byte)0x85;//без оператора приведения не компилится
bytes[1] = 0x01;
a = (short)(bytes[0] & 0x00FF);
a |= ((short)bytes[1]) << 8;
System.out.println("Value 2 = " + String.format("0x%04X", a) + "\r\n");
}
}

Конечно, аналог объединения можно сымитировать и в Java, но писанины много, да и доступ будет не на прямую, а через методы. Например, так:
class ByteShort
{
private short a = 0;
public void setFull(short d){ a = d; }
public short getFull(){return a; }
public void setByte(byte d, int index)
{
short mask = 0x00FF,
tmp = (short)(d & mask);
mask <<= index*8;
mask = (short)(~mask);
a &= mask;
a |= tmp;
}
public byte getByte(int index)
{
short tmp = (short)(a >> index*8);
return (byte)tmp;
}
}
Какой вывод можно сделать из всего этого? Я для себя вывод сделал следующий - из тех языков программирования, с которыми я знаком (Си, Си++, Java, Ada, Pascal, Fortran) пока нет такого, который был бы одинаково удобен как для быстрого создания приложений со сложной логикой, так и для написания программ взаимодействия с аппаратурой, и при этом безопасного. До этого случая на роль такого универсального языка (с моей точки зрения) претендовала Java, но счас... я прям в недоумении развожу руками.

2 коммент.:

Анонимный комментирует...

В Java'e исключили беззнаковые типы, но есть ещё операция сдвига, которая бывает разная — арифметическая (знаковый) и логическая (беззнаковый), посему в Java'e появились две операции сдвига вправо: >> и >>>.

Анонимный комментирует...

Ошибка была в присвоении байту отрицательного значения. В этом случае необходимо использовать тип short, т.е. тип с большим диапазоном

public static void main(String[] args)
{
short bytes[] = new short[2];

bytes[0] = 0x56;//младший байт
bytes[1] = 0x02;//старший байт
short a = 0;
a = bytes[0];
a |= bytes[1] << 8;
System.out.println("Value 1 = " + String.format("0x%04X", a) + "\r\n");
bytes[0] = 0x85;//без оператора приведения не компилится
bytes[1] = 0x01;
a = bytes[0];
a |= bytes[1] << 8;
System.out.println("Value 2 = " + String.format("0x%04X", a) + "\r\n");
}

Отправить комментарий