Каково использование flavorDimensions в build.gradle (Android Studio)? [Дубликат]

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

Преамбула

IEEE 754 номер двойной бинарной с плавающей запятой (binary64) представляет собой номер формы

value = (-1) ^ s * (1.m51m50 ... m2m1m0 ) 2 * 2e-1023

blockquote>

в 64 бит:

  • Первый бит является битом знака : 1, если число отрицательно, 0 в противном случае.
  • Следующие 11 бит являются показателем , который является offset на 1023. Другими словами, после чтение битов экспоненты из числа двойной точности 1023 должно быть вычтено для получения мощности двух.
  • Остальные 52 бита представляют собой значение (или мантисса). В мантиссе «подразумеваемый» 1. всегда пропускается, поскольку самый старший бит любого двоичного значения равен 1.

1 - IEEE 754 допускает концепцию с нулевым значением - +0 и -0 обрабатываются по-разному: 1 / (+0) - положительная бесконечность; 1 / (-0) - отрицательная бесконечность. Для нулевых значений биты мантиссы и экспоненты равны нулю. Примечание: нулевые значения (+0 и -0) явно не классифицируются как denormal2.

2 - Это не относится к денормальным номерам , которые имеют показатель смещения нуля (и подразумевается 0.). Диапазон денормальных чисел двойной точности dmin ≤ | x | ≤ dmax, где dmin (наименьшее представимое ненулевое число) составляет 2-1023 - 51 (≈ 4,94 * 10-324) и dmax (наибольшее денормальное число, для которого мантисса полностью состоит из 1 s) составляет 2-1023 + 1 - 2-1023 - 51 (≈ 2.225 * 10-308).


Превращение числа двойной точности в двоичный

Существует множество онлайн-конвертеров для преобразования двойной точности (например, в binaryconvert.com ), но здесь приведен пример кода C # для получения представления IEEE 754 для числа двойной точности (я разделяю три части с двоеточиями (: ):

public static string BinaryRepresentation(double value)
{
    long valueInLongType = BitConverter.DoubleToInt64Bits(value);
    string bits = Convert.ToString(valueInLongType, 2);
    string leadingZeros = new string('0', 64 - bits.Length);
    string binaryRepresentation = leadingZeros + bits;

    string sign = binaryRepresentation[0].ToString();
    string exponent = binaryRepresentation.Substring(1, 11);
    string mantissa = binaryRepresentation.Substring(12);

    return string.Format("{0}:{1}:{2}", sign, exponent, mantissa);
}

Достижение точки: исходный вопрос

(Перейти к нижней части для версии TL; DR)

Катон Джонстон (вопросник) спросил, почему 0.1 + 0.2! = 0.3.

Написано в двоичном (с двоеточиями, разделяющими три части), представления IEEE 754 значений:

0.1 => 0:01111111011:1001100110011001100110011001100110011001100110011010
0.2 => 0:01111111100:1001100110011001100110011001100110011001100110011010

Обратите внимание, что мантисса состоит из повторяющихся цифр 0011. ключ к тому, почему есть какие-либо ошибки в расчетах - 0,1, 0,2 и 0,3 не могут быть представлены в двоичной форме точно в конечном числе двоичных битов не более 1/9, 1/3 или 1/7 могут быть представлены точно в десятичных разрядах .

Преобразование экспонентов в десятичные, удаление смещения и повторное добавление подразумеваемых 1 (в квадратных скобках), 0,1 и 0,2 :

0.1 = 2^-4 * [1].1001100110011001100110011001100110011001100110011010
0.2 = 2^-3 * [1].1001100110011001100110011001100110011001100110011010

Чтобы добавить два числа, показатель должен быть одинаковым, т. е.

0.1 = 2^-3 *  0.1100110011001100110011001100110011001100110011001101(0)
0.2 = 2^-3 *  1.1001100110011001100110011001100110011001100110011010
sum = 2^-3 * 10.0110011001100110011001100110011001100110011001100111

Поскольку сумма не имеет вид 2n * 1. { bbb} мы увеличиваем показатель на единицу и сдвигаем десятичную ( двоичную ) точку, чтобы получить:

sum = 2^-2 * 1.0011001100110011001100110011001100110011001100110011(1)

В мантиссе сейчас 53 бит (53-й квадрат скобки в строке выше). Режим округления по умолчанию для IEEE 754 равен ' Round to Nearest ' - то есть, если число x падает между двумя значениями a и b выбрано значение, в котором наименьший значащий бит равен нулю.

a = 2^-2 * 1.0011001100110011001100110011001100110011001100110011
x = 2^-2 * 1.0011001100110011001100110011001100110011001100110011(1)
b = 2^-2 * 1.0011001100110011001100110011001100110011001100110100

Обратите внимание, что a и b отличаются только последним битом; ...0011 + 1 = ...0100. В этом случае значение с младшим значащим разрядом равно b , поэтому сумма равна:

sum = 2^-2 * 1.0011001100110011001100110011001100110011001100110100

TL; DR

Запись 0.1 + 0.2 в двоичном представлении IEEE 754 (с двоеточиями, разделяющими три части) и сравнивая его с 0.3, это (я положил отдельные биты в квадратные скобки):

0.1 + 0.2 => 0:01111111101:0011001100110011001100110011001100110011001100110[100]
0.3       => 0:01111111101:0011001100110011001100110011001100110011001100110[011]

Преобразован обратно к десятичной, эти значения:

0.1 + 0.2 => 0.300000000000000044408920985006...
0.3       => 0.299999999999999988897769753748...

Разница в точности равна 2-54, что составляет ~ 5.5511151231258 × 10-17 - незначительно (для многих приложений) по сравнению с исходными значениями.

Сравнение последних нескольких бит числа с плавающей запятой по своей сути опасно, как и любой, кто читает знаменитый «, что каждый компьютерный ученый должен знать о арифметике с плавающей точкой » (который охватывает все основные части этого ответа).

Большинство калькуляторов используют дополнительные охранные цифры , чтобы обойти эту проблему, так как 0.1 + 0.2 даст 0.3: последние несколько биты округлены.

17
задан Rab Ross 4 May 2016 в 09:56
поделиться

1 ответ

Я думаю, вы неправильно поняли концепцию flavorDimension.

Аромат-аромат - это что-то вроде категории вкуса , и каждая комбинация аромата из каждого измерения даст вариант.

В вашем случае вы должны определить один аромат, называемый «тип», и другое измерение, называемое «организация». Он будет производить для каждого аромата в «организации» измерения все возможные «типы» (или двойную формулировку: для каждого «типа» он будет давать вариант для каждой организации).

Размеры аромата определяют декартова произведение , которое будет использоваться для создания вариантов.


EDIT: я попытаюсь проиллюстрировать код псевдограду:

Давайте определим некоторый «тип»: бронза, серебро и золото

Давайте определим некоторые организации: customerA, customerB, customerC

Все это productFlavors , но они относятся к двум различным размерам:

flavorDimensions("type_line", "organization")
productFlavors {

    gold {
        ...
        dimension = "type_line"
    }
    silver {
        ...
        dimension = "type_line"
    }
    bronze {
         ...
        dimension = "type_line"
    }

     customerA {
        ...
        dimension = "organization"
    }
    customerB {
        ...
        dimension = "organization"
    }
    customerC {
         ...
        dimension = "organization"
    }

}

Эта конфигурация будет производят 18 (3 * 3 * 2) варианта (если у вас есть 2 стандартных типа сборки: отладка и выпуск):

gold-customerA-debug; gold-customerA-релиз; gold-customerB-debug; gold-customerB-релиз; gold-customerC-debug; gold-customerC-релиз;

silver-customerA-debug; серебро-клиентА-релиз; silver-customerB-debug; серебро-клиентB-релиз; silver-customerC-debug; серебро-клиентC-релиз;

... (то же самое для бронзы)

Обратите внимание, что имя измерения абсолютно произвольно и не влияет на имена вариантов.

Размеры вкуса являются очень мощными, но если вы используете слишком много из них: это приводит к экспоненциальному взрыву числа вариантов (задача очистки после сборки может быть полезна для удаления бесполезного или нечувствительного варианта)

33
ответ дан ben75 31 August 2018 в 22:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: