Float double java чем отличаются
Перейти к содержимому

Float double java чем отличаются

  • автор:

Float double java чем отличаются

Table of Contents

The Java programming language is a statically typed language, which means that every variable and every expression has a type that is known at compile time.

The Java programming language is also a strongly typed language, because types limit the values that a variable (§4.12) can hold or that an expression can produce, limit the operations supported on those values, and determine the meaning of the operations. Strong static typing helps detect errors at compile time.

The types of the Java programming language are divided into two categories: primitive types and reference types. The primitive types (§4.2) are the boolean type and the numeric types. The numeric types are the integral types byte , short , int , long , and char , and the floating-point types float and double . The reference types (§4.3) are class types, interface types, and array types. There is also a special null type. An object (§4.3.1) is a dynamically created instance of a class type or a dynamically created array. The values of a reference type are references to objects. All objects, including arrays, support the methods of class Object (§4.3.2). String literals are represented by String objects (§4.3.3).

4.1. The Kinds of Types and Values

There are two kinds of types in the Java programming language: primitive types (§4.2) and reference types (§4.3). There are, correspondingly, two kinds of data values that can be stored in variables, passed as arguments, returned by methods, and operated on: primitive values (§4.2) and reference values (§4.3).

There is also a special null type , the type of the expression null (§3.10.7, §15.8.1), which has no name.

Because the null type has no name, it is impossible to declare a variable of the null type or to cast to the null type.

The null reference is the only possible value of an expression of null type.

The null reference can always be assigned or cast to any reference type (§5.2, §5.3, §5.5).

In practice, the programmer can ignore the null type and just pretend that null is merely a special literal that can be of any reference type.

4.2. Primitive Types and Values

A primitive type is predefined by the Java programming language and named by its reserved keyword (§3.9):

Primitive values do not share state with other primitive values.

The numeric types are the integral types and the floating-point types.

The integral types are byte , short , int , and long , whose values are 8-bit, 16-bit, 32-bit and 64-bit signed two’s-complement integers, respectively, and char , whose values are 16-bit unsigned integers representing UTF-16 code units (§3.1).

The floating-point types are float , whose values include the 32-bit IEEE 754 floating-point numbers, and double , whose values include the 64-bit IEEE 754 floating-point numbers.

The boolean type has exactly two values: true and false .

4.2.1. Integral Types and Values

The values of the integral types are integers in the following ranges:

For byte , from -128 to 127, inclusive

For short , from -32768 to 32767, inclusive

For int , from -2147483648 to 2147483647, inclusive

For long , from -9223372036854775808 to 9223372036854775807, inclusive

For char , from ‘\u0000’ to ‘\uffff’ inclusive, that is, from 0 to 65535

4.2.2. Integer Operations

The Java programming language provides a number of operators that act on integral values:

The comparison operators, which result in a value of type boolean :

The numerical comparison operators < , <= , > , and >= (§15.20.1)

The numerical equality operators == and != (

The numerical operators, which result in a value of type int or long :

The unary plus and minus operators + and — (§15.15.3, §15.15.4)

The multiplicative operators * , / , and % (§15.17)

The additive operators + and — (§15.18)

The increment operator ++ , both prefix (§15.15.1) and postfix (§15.14.2)

The decrement operator — , both prefix (§15.15.2) and postfix (§15.14.3)

The signed and unsigned shift operators << , >> , and >>> (§15.19)

The bitwise complement operator

The integer bitwise operators & , ^ , and | (§15.22.1)

The conditional operator ? : (§15.25)

The cast operator (§15.16), which can convert from an integral value to a value of any specified numeric type

The string concatenation operator + (§15.18.1), which, when given a String operand and an integral operand, will convert the integral operand to a String representing its value in decimal form, and then produce a newly created String that is the concatenation of the two strings

Other useful constructors, methods, and constants are predefined in the classes Byte , Short , Integer , Long , and Character .

If an integer operator other than a shift operator has at least one operand of type long , then the operation is carried out using 64-bit precision, and the result of the numerical operator is of type long . If the other operand is not long , it is first widened (§5.1.5) to type long by numeric promotion (§5.6).

Otherwise, the operation is carried out using 32-bit precision, and the result of the numerical operator is of type int . If either operand is not an int , it is first widened to type int by numeric promotion.

Any value of any integral type may be cast to or from any numeric type. There are no casts between integral types and the type boolean .

See §4.2.5 for an idiom to convert integer expressions to boolean .

The integer operators do not indicate overflow or underflow in any way.

An integer operator can throw an exception (§11 (Exceptions)) for the following reasons:

Any integer operator can throw a NullPointerException if unboxing conversion (§5.1.8) of a null reference is required.

The integer divide operator / (§15.17.2) and the integer remainder operator % (§15.17.3) can throw an ArithmeticException if the right-hand operand is zero.

The increment and decrement operators ++ (§15.14.2, §15.15.1) and — (§15.14.3, §15.15.2) can throw an OutOfMemoryError if boxing conversion (§5.1.7) is required and there is not sufficient memory available to perform the conversion.

Example 4.2.2-1. Integer Operations

This program produces the output:

and then encounters an ArithmeticException in the division by l — i , because l — i is zero. The first multiplication is performed in 32-bit precision, whereas the second multiplication is a long multiplication. The value -727379968 is the decimal value of the low 32 bits of the mathematical result, 1000000000000 , which is a value too large for type int .

4.2.3. Floating-Point Types, Formats, and Values

The floating-point types are float and double , which are conceptually associated with the single-precision 32-bit and double-precision 64-bit format IEEE 754 values and operations as specified in IEEE Standard for Binary Floating-Point Arithmetic, ANSI/IEEE Standard 754-1985 (IEEE, New York).

The IEEE 754 standard includes not only positive and negative numbers that consist of a sign and magnitude, but also positive and negative zeros, positive and negative infinities , and special Not-a-Number values (hereafter abbreviated NaN). A NaN value is used to represent the result of certain invalid operations such as dividing zero by zero. NaN constants of both float and double type are predefined as Float.NaN and Double.NaN .

Every implementation of the Java programming language is required to support two standard sets of floating-point values, called the float value set and the double value set . In addition, an implementation of the Java programming language may support either or both of two extended-exponent floating-point value sets, called the float-extended-exponent value set and the double-extended-exponent value set . These extended-exponent value sets may, under certain circumstances, be used instead of the standard value sets to represent the values of expressions of type float or double (§5.1.13, §15.4).

The finite nonzero values of any floating-point value set can all be expressed in the form sm ⋅ 2 ( eN + 1) , where s is +1 or -1, m is a positive integer less than 2 N , and e is an integer between Emin = -(2 K -1 -2) and Emax = 2 K -1 -1, inclusive, and where N and K are parameters that depend on the value set. Some values can be represented in this form in more than one way; for example, supposing that a value v in a value set might be represented in this form using certain values for s , m , and e , then if it happened that m were even and e were less than 2 K -1 , one could halve m and increase e by 1 to produce a second representation for the same value v . A representation in this form is called normalized if m ≥ 2 N -1 ; otherwise the representation is said to be denormalized . If a value in a value set cannot be represented in such a way that m ≥ 2 N -1 , then the value is said to be a denormalized value , because it has no normalized representation.

The constraints on the parameters N and K (and on the derived parameters Emin and Emax ) for the two required and two optional floating-point value sets are summarized in Table 4.2.3-A.

Table 4.2.3-A. Floating-point value set parameters

Parameter float float-extended-exponent double double-extended-exponent
N 24 24 53 53
K 8 ≥ 11 11 ≥ 15
Emax +127 ≥ +1023 +1023 ≥ +16383
Emin -126 ≤ -1022 -1022 ≤ -16382

Where one or both extended-exponent value sets are supported by an implementation, then for each supported extended-exponent value set there is a specific implementation-dependent constant K , whose value is constrained by Table 4.2.3-A; this value K in turn dictates the values for Emin and Emax .

Each of the four value sets includes not only the finite nonzero values that are ascribed to it above, but also NaN values and the four values positive zero, negative zero, positive infinity, and negative infinity.

Note that the constraints in Table 4.2.3-A are designed so that every element of the float value set is necessarily also an element of the float-extended-exponent value set, the double value set, and the double-extended-exponent value set. Likewise, each element of the double value set is necessarily also an element of the double-extended-exponent value set. Each extended-exponent value set has a larger range of exponent values than the corresponding standard value set, but does not have more precision.

The elements of the float value set are exactly the values that can be represented using the single floating-point format defined in the IEEE 754 standard. The elements of the double value set are exactly the values that can be represented using the double floating-point format defined in the IEEE 754 standard. Note, however, that the elements of the float-extended-exponent and double-extended-exponent value sets defined here do not correspond to the values that can be represented using IEEE 754 single extended and double extended formats, respectively.

The float, float-extended-exponent, double, and double-extended-exponent value sets are not types. It is always correct for an implementation of the Java programming language to use an element of the float value set to represent a value of type float ; however, it may be permissible in certain regions of code for an implementation to use an element of the float-extended-exponent value set instead. Similarly, it is always correct for an implementation to use an element of the double value set to represent a value of type double ; however, it may be permissible in certain regions of code for an implementation to use an element of the double-extended-exponent value set instead.

Except for NaN, floating-point values are ordered ; arranged from smallest to largest, they are negative infinity, negative finite nonzero values, positive and negative zero, positive finite nonzero values, and positive infinity.

IEEE 754 allows multiple distinct NaN values for each of its single and double floating-point formats. While each hardware architecture returns a particular bit pattern for NaN when a new NaN is generated, a programmer can also create NaNs with different bit patterns to encode, for example, retrospective diagnostic information.

For the most part, the Java SE platform treats NaN values of a given type as though collapsed into a single canonical value, and hence this specification normally refers to an arbitrary NaN as though to a canonical value.

However, version 1.3 of the Java SE platform introduced methods enabling the programmer to distinguish between NaN values: the Float.floatToRawIntBits and Double.doubleToRawLongBits methods. The interested reader is referred to the specifications for the Float and Double classes for more information.

Positive zero and negative zero compare equal; thus the result of the expression 0.0==-0.0 is true and the result of 0.0>-0.0 is false. But other operations can distinguish positive and negative zero; for example, 1.0/0.0 has the value positive infinity, while the value of 1.0/-0.0 is negative infinity.

NaN is unordered , so:

The numerical comparison operators < , <= , > , and >= return false if either or both operands are NaN (§15.20.1).

The equality operator == returns false if either operand is NaN.

In particular, (x<y) == !(x>=y) will be false if x or y is NaN.

The inequality operator != returns true if either operand is NaN ( In particular, x!=x is true if and only if x is NaN.

В чем разница между float и double в Java?

Насколько я помню, float и double различаются диапазонами, что ведёт к разной точности и скорости вычислений. Однако сегодня заметил, что с разными числами округление происходит у этих типов данных по-разному. На скринах видно, что

  • в первом случае float обрубает значение, а double округляет (example1)
  • Во втором случае: float округляет, double обрубает значение(example2)

Какая еще есть разница между типами данных float и double или я что-то в упор не вижу?

Yuri Migushin's user avatar

Принципиальный нюанс здесь в том, что вы оперируете десятичными числами, а float и double — бинарные. Те числа, которые вы делите и получаете в результате, непредставимы в бинарной системе счисления без периодичных дробей. Подробнее:

То, что вы наблюдаете — это не округления и обрубания, а попытки компьютера впихать невпихуемое и сделать вид, что на самом деле всё в порядке. Так как количество битов у мантиссы и экспоненты типов float и double разная, то «округления» и «обрубания» будут происходить с вашей точки зрения «непредсказуемо». Эта ситуация усугубляется ещё и тем, что точность вычислений на совести процессора, который, вообще говоря, не обязан выдавать точность до последнего знака. На другом компьютере вы можете увидеть другие результаты — если не включите специальный «предсказуемый» режим, который медленнее. Но даже в этом случае вы получите только одинаковые, но «непредсказуемые» результаты на разных компьютерах.

Когда работаете с float и double, примите как данность, что они всегда с погрешностью.

Java Float vs Double

Today, we will discuss the topic of Java float vs double. In Java programming language, floating-point numbers represented by the float and double data types.

The float data type is a 32-bit IEEE 754 floating-point number with single accuracy. And the double data type is a 64-bit IEEE 754 floating-point number with double accuracy.

In Java, both double and float use to represent floating-point values. While there are both parallels and distinctions between the two.

The accuracy of a double may be up to 15 to 16 decimal places. But the accuracy of a float can be up to 6 or 7 decimal places.

In terms of storage requirements, doubling is more expensive. A variable needs 8 bytes to store in it, but a float variable takes 4 bytes to store.

If memory is a concern, the float is a better choice than the double. There are more differences to check between Java float vs double in the below section.

Need any kind of assignment or homework help? Hire Codeavail experts now!

Similarities in Java float and double

There are three similarities in float and double in Java. And these are as follows:

1. In Java, real numbers, or numbers having decimal values, are represented by the double and float data types.

2. They are both inaccurate and produce estimates.

3. Because these numbers are not clear, we should compare them using logical operators (> or <).

What are the differences between the two: Java Float vs Double

  1. A wrapper class is a kind of object that wraps or contains primitive data types in its object. When we construct an object for a wrapper class, it includes a field where we can keep primitive data types. In other words, we can say that a primitive value can wrap into the individual wrapper class object. Therefore, the wrapper class for double is java.lang. Double. The wrapper class for floats is java.lang. Float.
  2. A float data type holds a decimal value with 6–8 digits of accuracy. We should always include the letter “f” at the end of a float data type whenever representing it in Java. Else, it will save as a double. In Java, the default value for a float is 0.0f, whereas the default value for a double is 0.0d.
  3. The double data type is really a predefined floating-point data type. It contains a decimal, fractional, integer, or exponent component. The user has the option of representing floating-point data in either exponential or decimal form.

Floating-point is used in object-oriented programming to represent decimal numbers with the “fractional portion.” And the default floating-point data type is “double.”

  1. Although both the float and double data types are used in Java to express floating-point values, the double data type is more precise. In comparison to floating accuracy of 6 to 7 decimal digits, a double variable may give precision of up to 15 to 16 decimal points.
  2. A 64-bit floating-point number is referred to as a “double.” It accepts eight bytes of RAM (64 bits). A 32-bit floating-point digit is referred to as a “float.” It takes up four bytes of memory (32 bits). Java represents it in this way. Internally, though, things are different.

For the time being, I’ll focus on x86 because it’s the most prevalent architecture on which the JVM operates. On occasion, Java uses the x87 (not x86) floating-point stack. The x87 is a “floating-point coprocessor,” with an 80-bit (10-byte) float memory. The JVM may allocate 10 bytes for one double, but the bytecode will only see eight bytes.

  1. In float, the exponent is 8 bits, the sign is 1 bit, and the mantissa is 23 bits, but in double, the exponent is 11 bits, the sign is 1 bit, and the mantissa is 52 bits.

These are the six major differences that can differentiate float and double in Java.

When should you use Java float vs double?

Simply when you write 2.3, the compiler will consider this a double, but when you write 2.5, the compiler will consider this as a float.

And 2.5 is 10.101

In computer languages, the terms “float” and “double” both use to denote floating-point values. Their range and the amount of bits/bytes they use are the key differences between them.

In Java, float takes up four bytes and has a smaller range than double, which takes up eight. Precision up to 6 decimal places provide by float, whereas precision up to 16 decimal places provided by double.

  • If memory isn’t an issue or we need greater accuracy than 7, double is the way to go.
  • If memory is an issue, we should utilize float rather than double.

Let’s wrap it up!

That’s all there is to it when it comes to the differences between Java float vs double. Remember that in Java, floating-point values will double by default. If you wish to save them in float variables, you must either manually cast them or suffix them with the ‘f’ or ‘F’ character.

It also recommends practice to use a data type. It requires less storage if it’s enough for the data you’re saving, so choose float over double. But if accuracy and range are important to you; double is more accurate than float.

Thank you for taking the time to read this lesson so far. If you like this lesson, please tell your friends and coworkers about it. Please leave a comment if you have any questions, doubts, recommendations, or criticism, and I’ll do my best to respond to the queries.

Устройство вещественных чисел

Устройство вещественных чисел - 1

Привет! В сегодняшней лекции расскажем о числах в Java, а конкретно — о вещественных числах. Без паники! 🙂 Никаких математических сложностей в лекции не будет. Будем говорить о вещественных числах исключительно с нашей, «программистской» точки зрения. Итак, что же такое «вещественные числа»? Вещественные числа — это числа, у которых есть дробная часть (она может быть нулевой). Они могут быть положительными или отрицательными. Вот несколько примеров: 15 56.22 0.0 1242342343445246 -232336.11 Как же устроено вещественное число? Достаточно просто: оно состоит из целой части, дробной части и знака. У положительных чисел знак обычно не указывают явно, а у отрицательных указывают. Ранее мы подробно разобрали, какие операции над числами можно совершать в Java. Среди них было много стандартных математических операций — сложение, вычитание и т. д. Было и кое-что новое для тебя: например, остаток от деления. Но как именно устроена работа с числами внутри компьютера? В каком виде они хранятся в памяти?

Хранение вещественных чисел в памяти

Тип Размер в памяти (бит) Диапазон значений
byte 8 бит от -128 до 127
short 16 бит от -32768 до 32767
char 16 бит беззнаковое целое число, которое преставляет собой символ UTF-16 (буквы и цифры)
int 32 бита от -2147483648 до 2147483647
long 64 бита от -9223372036854775808 до 9223372036854775807
float 32 бита от 2 -149 до (2-2 -23 )*2 127
double 64 бита от 2 -1074 до (2-2 -52 )*2 1023

Устройство вещественных чисел - 2

Сегодня поговорим именно о последних двух типах — float и double . Оба выполняют одну и ту же задачу — представляют дробные числа. Их еще очень часто называют « числа с плавающей точкой» . Запомни этот термин на будущее 🙂 Например, число 2.3333 или 134.1212121212. Довольно странно. Ведь получается, нет никакой разницы между этими двумя типами, раз они выполняют одну и ту же задачу? Но разница есть. Обрати внимание на столбец «размер в памяти» в таблице выше. Все числа (да и не только числа — вообще вся информация) хранится в памяти компьютера в виде битов. Бит — это самая маленькая единица измерения информации. Она довольно проста. Любой бит равен или 0, или 1. Да и само слово « bit » происходит от английского « binary digit » — двоичное число. Думаю, ты наверняка слышал о существовании двоичной системы счисления в математике. Любое привычное нам десятичное число можно представить в виде набора единиц и нулей. Например, число 584.32 в двоичной системе будет выглядеть так: 100100100001010001111 . Каждые единица и ноль в этом числе являются отдельным битом. Теперь тебе должна быть более понятна разница между типами данных. Например, если мы создаем число типа float , в нашем распоряжении есть всего 32 бита. При создании числа float именно столько места будет выделено для него в памяти компьютера. Если же мы хотим создать число 123456789.65656565656565, в двоичном виде оно будет выглядеть так: 11101011011110011010001010110101000000 . Оно состоит из 38 единиц и нулей, то есть для его хранения в памяти нужно 38 бит. В тип float это число просто не «влезет»! Поэтому число 123456789 можно представить в виде типа double . Для его хранения выделяется целых 64 бита: это нам подходит! Разумеется, и диапазон значений тоже будет подходящим. Для удобства ты можешь представлять число как маленький ящик с ячейками. Если ячеек хватает для хранения каждого бита, значит, тип данных выбран правильно 🙂 Разумеется, разное количество выделяемой памяти влияет и на само число. Обрати внимание, что у типов float и double отличается диапазон значений. Что это означает на практике? Число double может выразить большую точность, чем число float . У 32-битных чисел с плавающей точкой (в Java это как раз тип float ) точность составляет примерно 24 бита, то есть около 7 знаков после запятой. А у 64-битных чисел (в Java это тип double ) — точность примерно 53 бита, то есть примерно 16 знаков после запятой. Вот пример, который хорошо демонстрирует эту разницу: Что мы должны получить здесь в качестве результата? Казалось бы, все довольно просто. У нас есть число 0.0, и мы 7 раз подряд прибавляем к нему 0.1111111111111111. В итоге должно получиться 0.7777777777777777. Но мы создали число float . Его размер ограничен 32 битами и, как мы сказали ранее, он способен отобразить число примерно до 7 знака после запятой. Поэтому в итоге результат, который мы получим в консоли, будет отличаться от того, что мы ожидали: Число как будто было «обрезано». Ты уже знаешь как хранятся данные в памяти — в виде битов, поэтому тебя не должно это удивлять. Понятно, почему это произошло: результат 0.7777777777777777 просто не влез в выделенные нам 32 бита, поэтому и был обрезан так, чтобы поместиться в переменную типа float 🙂 Мы можем изменить тип переменной на double в нашем примере, и тогда итоговый результат не будет обрезан: Здесь уже 16 знаков после запятой, результат «уместился» в 64 бита. Кстати, возможно ты заметил, что в обоих случаях результаты получились не совсем корректными? Подсчет был произведен с небольшими ошибками. О причинах этого мы поговорим ниже 🙂 Теперь скажем пару слов о том, как можно сравнить числа между собой.

Сравнение вещественных чисел

Устройство вещественных чисел - 3

Мы частично уже затрагивали этот вопрос в прошлой лекции, когда говорили об операциях сравнения. Такие операции как > , < , >= , <= повторно разбирать мы не будем. Вместо этого рассмотрим более интересный пример: Как ты думаешь, какое число будет выведено на экран? Логичным ответом был бы ответ: число 1. Мы начинаем отсчет с числа 0.0 и последовательно прибавляем к нему 0.1 десять раз подряд. Вроде все правильно, должна получиться единица. Попробуй запустить этот код, и ответ сильно тебя удивит 🙂 Вывод в консоль: Но почему в таком простом примере возникла ошибка? О_о Тут бы даже пятиклассник с легкостью верно ответил, но программа на Java выдала неточный результат. «Неточный» тут более подходящее слово, чем «неправильный». Мы все-таки получили очень близкое к единице число, а не просто какое-то рандомное значение 🙂 Оно отличается от правильного буквально на миллиметр. Но почему? Возможно, это просто разовая ошибка. Может, комп заглючил? Попробуем написать другой пример. Вывод в консоль: Так, дело явно не в глюках компа 🙂 Что происходит? Подобные ошибки связаны с тем, как числа представлены в двоичном виде в памяти компьютера. Дело в том, что в двоичной системе невозможно точно представить число 0,1 . В десятичной системе, кстати, тоже есть подобная проблема: в ней нельзя правильно представить дроби (и вместо ⅓ мы получим 0.33333333333333…, что тоже не совсем правильный результат). Казалось бы, мелочь: при таких подсчетах разница может быть в одну стотысячную часть (0,00001) или даже меньше. Но что, если от этого сравнения будет зависеть весь результат работы твоей Очень Серьезной Программы? Мы явно ожидали, что два числа будут равны, но из-за особенностей внутреннего устройства памяти мы отменили запуск ракеты. Раз так, нам нужно определиться, как же все-таки сравнить два числа с плавающей точкой, чтобы результат сравнения был более. эммм. предсказуемым. Итак, правило №1 при сравнении вещественных чисел мы уже усвоили: никогда не используй == при сравнении чисел с плавающей точкой. Ок, плохих примеров, думаю, достаточно 🙂 Давай рассмотрим хороший пример! Здесь мы по сути делаем то же самое, но меняем способ сравнения чисел. У нас есть специальное «пороговое» число — 0.0001, одна десятитысячная. Оно может быть и другим. Это зависит от того, насколько точное сравнение тебе нужно в конкретном случае. Можно сделать его и больше, и меньше. С помощью метода Math.abs() мы получаем модуль числа. Модуль — это значение числа независимо от знака. Например, у чисел -5 и 5 модуль будет одинаковым и равен 5. Мы вычитаем второе число из первого, и если полученный результат, независимо от знака, будет меньше того порога, который мы установили, значит наши числа равны. Во всяком случае, они равны до той степени точности, которую мы установили с помощью нашего «порогового числа» , то есть как минимум они равны вплоть до одной десятитысячной. Такой способ сравнения избавит тебя от неожиданного поведения, которое мы увидели в случае с == . Еще один хороший способ сравнения вещественных чисел — использовать специальный класс BigDecimal . Этот класс специально был создан для хранения очень больших чисел с дробной частью. В отличие от double и float , при использовании BigDecimal сложение, вычитание и прочие математические операции выполняются не с помощью операторов ( +- и т.д.), а с помощью методов. Вот как это будет выглядеть в нашем случае: Какой же вывод в консоль мы получим? Мы получили ровно тот результат, на который рассчитывали. И обрати внимание, насколько точными получились наши числа, и сколько знаков после запятой в них уместилось! Гораздо больше, чем во float и даже в double ! Запомни класс BigDecimal на будущее, он тебе обязательно пригодится 🙂 alt=»Устройство вещественных чисел — 4″ width=»1024″ />Фух! Лекция получилась немаленькая, но ты справился: молодец! 🙂 Увидимся на следующем занятии, будущий программист!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *