// PascalABC.NET 3.3, сборка 1611 от 06.01.2018 // Внимание! Если программа не работает, обновите версию!
type Exams=record fam:string; nam:string; ball:=new integer[4]; end;
begin var Ls:=new List<Exams>; var b:Exams; // буфер ReadLines('marks.csv') .Foreach(t->begin var a:=t.ToWords(','); (b.fam,b.nam):=(a[0],a[1]); b.ball:=a[2:].Select(q->q.ToInteger).ToArray; Ls.Add(b) end); var n:=Ls.Count; // а var m:=Arr(0,0,0,0); var c:=Ls.Select(t->t.Ball); foreach var d in c do for var i:=0 to 3 do m[i]+=d[i]; Writeln('Средние по предметам: ', m[0]/n:0:1,', ',m[1]/n:0:1, ', ',m[2]/n:0:1,', ',m[3]/n:0:1); // б var max:=Ls.Select(t->t.Ball.Sum).Max; Writeln(NewLine,'Максимальная сумма ',max); // в Writeln(NewLine,'Учащиеся, набравшие максимальную сумму :'); Ls.Where(t->t.Ball.Sum=max).Select(t->t.fam+' '+t.nam) .OrderBy(t->t).Println(NewLine); // г Writeln(NewLine,'Получили хотя бы одну двойку:'); Ls.Where(t->t.Ball.Any(t->t=2)).Select(t->t.fam+' '+t.nam) .OrderBy(t->t).Println(NewLine) end.
Пример: Средние по предметам: 3.3, 3.3, 4.0, 2.0
Максимальная сумма 13
Учащиеся, набравшие максимальную сумму : Алескеров Имран Бармин Владимир
Получили хотя бы одну двойку: Александрова Маргарита Алескеров Имран Бармин Владимир
Замечание: В PascalABC.NET 3.3 динамические массивы реализованы с списков, поэтому было принято решение использовать именно список, позволяющий не знать заранее размера массива и добавлять элементы по мере необходимости. Если возникнет причуда иметь данные именно в массиве, перед end. добавьте строку var x:=Ls.ToArray; - она создаст из списка массив х.
1. 16-битная арифметика со знаком предполагает, что самый левый бит используется для хранения знака. Отрицательные числа хранятся в дополнительном коде. При этом диапазон представления данных составляет -32768..32767. 32760+9 можно записать как 32767+2. Это позволит избежать перевода 32760 в двоичную систему счисления, а 32767 - это 15 двоичных единиц. В знаковом разряде, конечно же, ноль. После сложения в знаковом разряде появляется единица, что означает наличие отрицательного числа в дополнительном коде. Знаковый разряд мы не трогаем, а остальные инвертируем и арифметически прибавляем к полученному числу единицу. Тем самым переходим к прямому коду, который переводим в десятичную систему представления. И результат, конечно, же, будет со знаком минус, т.е. -32767. Вот к чему приводит переполнение разрядной сетки в целочисленной арифметике. Кстати, аппаратно оно не обнаруживается, поскольку криминала нет - просто +1 переходит в самый старший (левый) разряд. "Железо" ведь не знает, сколько разрядов мы отвели под представление чисел и как биты нужно рассматривать! Соответствующая картинка находится в первом вложении.
2. В восьмибитной арифметике все происходит аналогично. 127 представляется знаковым нулем и семью единицами в остальных разрядах, т.е. 01111111₂. Тройка - это 0..011₂ Складываем и получаем 10000010₂. Опять знаковый разряд единичный, инвертируем остальные: 11111101. А теперь прибавляем единицу и получаем 11111110₂. Числу 1111110₂ (знаковый разряд мы не учитываем) соответствует 126₁₀, а с учетом знака окончательно получаем -126.
3. Тут немного больше нужно повозиться. Арифметика снова 16-битная, диапазон представления чисел -32768..32767. Выпишем факториалы в пределах этого диапазона и одно значение вне его. 1!=1, 2!=2, 3!=6, 4!=24, 5"=120, 6!=720, 7!=5040, 8!=40320. Делаем вывод, что максимальное значение факториала можно вычислить для n=7 и n!=5040. Тогда n+1=8 и при его вычислении у нас возникнет арифметическое переполнение. Переведем число 5040 в двоичную систему и умножим его на 8, поскольку 8! = 7! × 8. Поскольку 8 = 2³, то умножение на 8 в двоичной системе равносильно сдвигу числа влево на три разряда. Подробности приведены на рисунке во втором вложении. Мы получим "странный" результат: 8! = -25216.
// Внимание! Если программа не работает, обновите версию!
type
Exams=record
fam:string;
nam:string;
ball:=new integer[4];
end;
begin
var Ls:=new List<Exams>;
var b:Exams; // буфер
ReadLines('marks.csv')
.Foreach(t->begin
var a:=t.ToWords(',');
(b.fam,b.nam):=(a[0],a[1]);
b.ball:=a[2:].Select(q->q.ToInteger).ToArray;
Ls.Add(b)
end);
var n:=Ls.Count;
// а
var m:=Arr(0,0,0,0);
var c:=Ls.Select(t->t.Ball);
foreach var d in c do
for var i:=0 to 3 do m[i]+=d[i];
Writeln('Средние по предметам: ', m[0]/n:0:1,', ',m[1]/n:0:1,
', ',m[2]/n:0:1,', ',m[3]/n:0:1);
// б
var max:=Ls.Select(t->t.Ball.Sum).Max;
Writeln(NewLine,'Максимальная сумма ',max);
// в
Writeln(NewLine,'Учащиеся, набравшие максимальную сумму :');
Ls.Where(t->t.Ball.Sum=max).Select(t->t.fam+' '+t.nam)
.OrderBy(t->t).Println(NewLine);
// г
Writeln(NewLine,'Получили хотя бы одну двойку:');
Ls.Where(t->t.Ball.Any(t->t=2)).Select(t->t.fam+' '+t.nam)
.OrderBy(t->t).Println(NewLine)
end.
Пример:
Средние по предметам: 3.3, 3.3, 4.0, 2.0
Максимальная сумма 13
Учащиеся, набравшие максимальную сумму :
Алескеров Имран
Бармин Владимир
Получили хотя бы одну двойку:
Александрова Маргарита
Алескеров Имран
Бармин Владимир
Замечание:
В PascalABC.NET 3.3 динамические массивы реализованы с списков, поэтому было принято решение использовать именно список, позволяющий не знать заранее размера массива и добавлять элементы по мере необходимости. Если возникнет причуда иметь данные именно в массиве, перед end. добавьте строку var x:=Ls.ToArray; - она создаст из списка массив х.
32760+9 можно записать как 32767+2. Это позволит избежать перевода 32760 в двоичную систему счисления, а 32767 - это 15 двоичных единиц. В знаковом разряде, конечно же, ноль.
После сложения в знаковом разряде появляется единица, что означает наличие отрицательного числа в дополнительном коде. Знаковый разряд мы не трогаем, а остальные инвертируем и арифметически прибавляем к полученному числу единицу. Тем самым переходим к прямому коду, который переводим в десятичную систему представления. И результат, конечно, же, будет со знаком минус, т.е. -32767. Вот к чему приводит переполнение разрядной сетки в целочисленной арифметике. Кстати, аппаратно оно не обнаруживается, поскольку криминала нет - просто +1 переходит в самый старший (левый) разряд. "Железо" ведь не знает, сколько разрядов мы отвели под представление чисел и как биты нужно рассматривать! Соответствующая картинка находится в первом вложении.
2. В восьмибитной арифметике все происходит аналогично. 127 представляется знаковым нулем и семью единицами в остальных разрядах, т.е. 01111111₂. Тройка - это 0..011₂
Складываем и получаем 10000010₂. Опять знаковый разряд единичный, инвертируем остальные: 11111101. А теперь прибавляем единицу и получаем 11111110₂. Числу 1111110₂ (знаковый разряд мы не учитываем) соответствует 126₁₀, а с учетом знака окончательно получаем -126.
3. Тут немного больше нужно повозиться. Арифметика снова 16-битная, диапазон представления чисел -32768..32767.
Выпишем факториалы в пределах этого диапазона и одно значение вне его. 1!=1, 2!=2, 3!=6, 4!=24, 5"=120, 6!=720, 7!=5040, 8!=40320.
Делаем вывод, что максимальное значение факториала можно вычислить для n=7 и n!=5040. Тогда n+1=8 и при его вычислении у нас возникнет арифметическое переполнение. Переведем число 5040 в двоичную систему и умножим его на 8, поскольку 8! = 7! × 8. Поскольку 8 = 2³, то умножение на 8 в двоичной системе равносильно сдвигу числа влево на три разряда. Подробности приведены на рисунке во втором вложении. Мы получим "странный" результат: 8! = -25216.