Расширение с байта до интервала (ошибка Marshmallow)

Извините заранее за длинный вопрос, но все должно быть прямолинейно и понятно, что происходит, спасибо, что посмотрели. Обратите внимание, что это не код, а просто псевдокод, чтобы понять реализацию приложения.

вопрос

Байт не расширяется до истинного численного значения. Примечание: level = -1 представляет игру, которая еще не началась. level == 24 представляет собой конец игры.

Класс 1

 private byte level = -1; private byte stage = 0; @NonNull private final Stages[][] stages = new Stages[25][13]; public byte getLevel () { return level; } public void nextLevel () { // expect: 0 level++; // stages[ 128][ 0] (ArrayIndexOutOfBounds) stages[level][stage] = new Stage(); } 

Класс 2 расширяет класс 1

 @NonNull private final byte[][] values = new byte[25][4]; public byte getScore (final byte level, final byte player) { // values[ 255][ 2] (ArrayIndexOutOfBounds) return values[level][player]; } // expecting: -1 >= 0 (false) // runtime: 255 >= 0 (true) if (Class1.getLevel() >= 0) getScore(Class1.getLevel(), 2); 

двоичный

8 бит (байт)

 -1 == 1111 1111 -128 == 1000 0000 127 == 0111 1111 

32 бит (целое число)

 -1 == 1111 1111 1111 1111 1111 1111 1111 1111 127 == 0000 0000 0000 0000 0000 0000 0111 1111 128 == 0000 0000 0000 0000 0000 0000 1000 0000 255 == 0000 0000 0000 0000 0000 0000 1111 1111 

Что работает

Использование класса оболочки

 public Byte level = -1; 

Вопрос

Я понимаю, что проблема заключается в том, что двоичное представление числа используется напрямую, когда оно расширяется с байта на int. Мой номер буквально идет от 8 бит 1111 1111 до 32 бит 0000 0000 0000 0000 0000 0000 1111 1111 . Мой вопрос в том, почему Java (или почему это не в этой ситуации / окружении) преобразует число в истинное числовое значение при расширении вместо нулей нулевого уровня в исходное двоичное представление.

Похоже, что это происходит только с отрицательными числами, я полагаю, что положительные числа имеют одинаковое представление бит до и после расширения.

Почему мой номер не идет от 8 бит 1111 1111 до 32 бит 1111 1111 1111 1111 1111 1111 1111 1111 ? Почему приращение postfix создает значение 128 ..? Есть ли лучшее решение этой проблемы рядом с тем, с которым я сейчас застрял?

Я предпочитаю не просто использовать это решение, не зная проблему подчеркивания, потому что проблема может спокойно (бежать без ошибок) нарушать алгоритм моих приложений; Поэтому я с большой благодарностью могу рассказать об этом мне.

Спасибо, Джей

Текущая рабочая среда

JDK 1.8.076

OS X El Capitan

Android Studio 2.2 Preview 2

BuildToolsVersion '23 .0.3 '

Classpath 'com.android.tools.build:gradle:2.2.0-alpha2'

Эмулятор Nexus 6P API 23 x86

Вывод

Я смог сузить проблему до устройств Android 23 (Marshmallow). Я сообщил об ошибке Google Inc. Спасибо за помощь, я просто предоставлю предупреждение пользователю Android 23 (Marshmallow), поскольку ошибка не появляется в Android N, Android 22 и ниже.

Почему бы не перевести подписанные байты в unsigned?

 public static int signedByteToInt(byte b) { return b & 0xFF; } 

Чтобы убедиться, что вы здесь, являются образцами представления в подписанном байте:

 -3 - 11111101 -2 - 11111110 -1 - 11111111 0 - 00000000 1 - 00000001 2 - 00000010 3 - 00000011 

И когда вы используете байт как int, java будет в любом случае представлять этот байт как подписанный, поэтому от 11111111 (-1) вы получите 11111111 11111111 11111111 11111111 (-1), и я не вижу здесь никаких проблем.

Просто сохраните в памяти, что:

 from -1 to -128 is from 11111111 to 10000000 and from 0 to 127 is from 00000000 to 01111111 

И сделать правильное преобразование, когда вы используете его как int.

Поэтому ниже нулевых представлений, как обратный счетчик к среднему

И, кстати, это не только в java)

Вот:

 if (Class1.getLevel() >= 0) 

Вы сравниваете байт с целым числом, попробуйте сделать:

 if (Class1.getLevel() >= (byte)0) 

И чувствую себя счастливым 🙂

Джей, я видел ваш код, и я проверил документацию для языка Java.

Для меня поведение, которое вы описываете, выглядит следующим образом:

 private byte level = -1; private byte stage = 0; 

Вы инициализируете байты до -1 и 0. Позже вы вызываете nextLevel() и ожидаете его на stage[0][0] init stage[0][0] , но вместо этого вы получаете исключение «за пределами». Таким образом, первый уровень никогда не достигается.

 public void nextLevel() { // expect: 0 level++; // stages[ 128][ 0] (ArrayIndexOutOfBounds) stages[level][stage] = new Stage(); } 

Если код, который вы не показывали в своем примере, не создает побочных эффектов, создающих ситуацию, следующий код является рабочим образцом, который должен быть достаточным, чтобы выявить проблему:

 package com.bytemagic; import com.sun.istack.internal.NotNull; /** * Created by thst on 01.06.2016. */ public class ByteMagic { private class Stage { } private byte level = -1; private byte stage = 0; @NotNull private final Stage[][] stages = new Stage[25][13]; public byte getLevel() { return level; } public void nextLevel() { // expect: 0 level++; // stages[ 128][ 0] (ArrayIndexOutOfBounds) stages[level][stage] = new Stage(); } public static void main(String... args) { ByteMagic me = new ByteMagic(); me.nextLevel(); System.out.println(Integer.toHexString(me.getLevel())); } } 

Я пробовал этот код с JDK8u66, JDK8u77. Это не вызывает никаких проблем. Уровень байта будет правильно увеличен до 0, а stages будут инициализированы правильно.

Не могли бы вы попытаться запустить этот код на своем компьютере и настроить? Этот код подвержен проблеме?

В конце концов: level не может удерживать 128, он может содержать -128, но это не то, что вы описываете.

level++ будет делать всевозможные смешные преобразования. Согласно документам, для вычисления приращения 1 level преобразуется в целое число с соответствующим расширением, если это необходимо (поэтому 0xff -> 0xffffffff), затем добавляется 1, что приводит к 0x0 (int). Затем он сужается до 0x0 (байт) и записывается на level . ( https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.2 )

Доступ к массиву использует несколько другое правило преобразования. Для доступа к массиву индекс должен иметь тип integer. Преобразование из байта в int использует унарные числовые правила продвижения, которые могут иметь интересный эффект конверсии при использовании унарного минуса, но для вашего случая с байтовым значением 0 ничего неожиданного или неожиданного не произойдет.

(Для справки: https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.6.1 и https://docs.oracle.com/javase/specs/ Jls / se8 / html / jls-10.html # jls-10.4 и https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.10.3 )

Таким образом, я не могу воспроизвести ошибку с указанными образцами кода, и мое подозрение идет в направлении, что это либо какой-то побочный эффект кода, который я не вижу, либо это ошибка в используемом JDK или в среде (Dalvik VM) ,

Intereting Posts
Поворот в ImageView, внутри RelativeLayout Прослушивание жестов в виджетах приложения Кордова запускает (в реальном) Android-устройство с помощью командной строки? Панель инструментов AndroidNavigationIcon не работает Извлекать только 20 строк с сервера с помощью setOnScrollListener Подписанное экспортированное приложение Android, не выполняющееся с классом apache.commons.logging, не может быть преобразовано в 'aaaac' Эффект поднятия Android L в Pre L (только с использованием свойства высоты) Как получить уровень компенсации экспозиции с камеры телефона Android через Java, когда снимок сделан? Запустить новый Hangouts Dialer от намерения Избегайте андроида отключать экран при включенном приложении (с помощью Phonegap) Как исправить "java.lang.ClassCastException: com.android.layoutlib.bridge.android.BridgeContext не может быть добавлен в исключение android.app.Activity" Различия между IntelliJ IDEA 13 и Android Studio AndroidJNIClass не может отключить мою нестационарную функцию Как записывать видео с фона приложения: Android LibGDX: создать сетку для настольной игры