Программа скомпилируется будет вызвана функция double square float value

В программировании то и дело случается писать функции для схожих действий, выполняемых над различными типами и наборами данных. Возьмите, например, функцию, которая должна возвращать квадрат своего аргумента. В C/C++ возведение в квадрат целого и числа с плавающей точкой – существенно разные операции.

Вообще говоря, придется написать две функции – одну, принимающую целый аргумент и возвращающую целое, и вторую, принимающую тип double и возвращающую также double. В С функции должны иметь уникальные имена. Таким образом, перед программистом встает задача придумывать массу имен для различных функций, выполняющих аналогичные действия. Например, SquareInt () и SquareDbl () .

В C++ допускаются перегруженные имена функций (термин взят из лингвистики), когда функции с одним именем можно, тем не менее, идентифицировать по их списку параметров, т.е. контексту, в котором имя употребляется.

Рассмотрим следующий пример с возведением переменных разного типа в квадрат. В нем предусмотрено еще «возведение в квадрат” строки, когда результатом функции должна быть строка, в которой любые символы, кроме пробелов, удваиваются.

The double and float Data Types in Java

int Square(int arg)

return arg*arg;

double Square(double arg)

return arg*arg;

char *Square(const char *arg, int n)

static char res[256];

int main(void)

double у = 3.1416;

char msg[] = «Output from overloaded Function!»;

printf(«Output: %d, %f, %sn», Square (x) , Square (y) , Square (msg, 32) ) ;

Результат работы программы показан на рис. 6.1.

Рис. 6.1 Пример с тремя перегруженными функциями

При вызове перегруженной функции компилятор определяет, какую именно функцию требуется вызвать, по типу фактических параметров. Этот процесс называется разрешением перегрузки (перевод английского слова resolution в смысле «уточнение»). Тип возвращаемого функцией значения в разрешении не участвует. Механизм разрешения сводится к тому, чтобы использовать функцию с наиболее подходящими аргументами и выдать сообщение, если такой не найдется. Допустим, имеется четыре варианта функции, определяющей наибольшее значение:

// Возвращает наибольшее из двух целых:

int max ( int , int ):

// Возвращает подстроку наибольшей длины:

char * max ( char *, char *);

// Возвращает наибольшее из первого параметра и длины второго:

int max ( int , char *):

// Возвращает наибольшее из второго параметра и длины первого:

int max (char*, int);

void f(int a, int b, char* c, char* d)

При вызове функции max компилятор выбирает соответствующий типу фактических параметров вариант функции (в приведенном примере будут последовательно вызваны все четыре варианта функции).

Если точного соответствия не найдено, выполняются продвижения порядковых типов в соответствии с общими правилами преобразования типов, например, bool и char в int , float в double и т. п. Далее выполняются стандартные преобразования типов, например, int в double или указателей в void *. Следующим шагом является выполнение преобразований типа, заданных пользователем, а также поиск соответствий за счет переменного числа аргументов функций. Если соответствие на одном и том же этапе может быть получено более чем одним способом, вызов считается неоднозначным и выдается сообщение об ошибке.

C# Programming Tutorial 13 — Float, Double, Decimal

Неоднозначность может появиться при:

— использовании аргументов по умолчанию.

Пример неоднозначности при преобразовании типа:

float f(float i)

double f(double i)

float x = 10.09:

double у = 10.09;

Для устранения этой неоднозначности требуется явное приведение типа для константы 10.

Читайте также:
Программа autocad требования к компьютеру

Пример неоднозначности при использовании параметров-ссылок: если одна из перегружаемых функций объявлена как int f ( int a , int b ), а другая – как int f ( int a , int ).

Нельзя перегружать функции, отличающиеся только типом возвращаемого значения.

Ничего сложного в механизме перегруженных функций нет. Перегруженные функции являются, по сути, совершенно различными функциями, идентифицируемыми не только своим именем (оно у них одно и то же), но и списком параметров. Компилятор выполняет т. н. декорирование имен. дополняя имя функции кодовой последовательностью символов, кодирующей тип ее параметров. Тем самым формируется уникальное внутреннее имя.

Вот несколько примеров того, как для различных прототипов производится декорирование имени функции:

Тип возвращаемого значения никак не отражается на декорировании имени.

Источник: h-l-l.ru

Возврат значений типа float

В тех случаях, когда значения, возвращаемые функцией, относятся к типу целочисленных или символьных, определение типа перед именем функции не является строго обязательным. Си изначально построен таким образом, чтобы воспринимать только данные типа int или char, так что, если тип не указан, Си посчитает, что возвращаемое значение относится к типу int или char.

Замечания по Си++

Перегрузка— это процесс в Си++, позволяющий операторам и функциям работать одновременно с данными разных типов. Можно использовать одно и то же имя для нескольких функций, как показано в следующих прототипах:

int doubles(int num);

float doubles(float num);

В программе будут присутствовать две функции doubles(), одна из которых удваивает значения данных типа int, а другая— данных типа float.

Рис. 7.8. Определение функции типа float

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

  1. Указать тип float перед именем функции.
  2. Определить саму функцию.

Использование return() в функции main()

Возможно, вы задумались над тем, что означает запись return(0) в функции main(). Обычно мы используем эту инструкцию, чтобы возвратить значение функции, но куда же мы передаем 0, когда программа заканчивается? Ответ прост: мы возвращаем его операционной системе. При запуске программы на языке Си можно считать, что операционная система вызывает функцию main().

Когда программа заканчивает выполнение, инструкция return() возвращает управление в систему. Параметр инструкции return() может, например, сообщать системе, имела ли место ошибка и что это была за ошибка. В этом случае запись return(0) сообщит, что ошибок не было. Некоторые программы могут возвращать и другие значения с тем, чтобы проинформировать операционную систему о возникших во время выполнения ошибках. В этом случае появится возможность выполнения дополнительных действий в зависимости от того, каким образом была прекращена работа программы*.

Использование макроопределений

Вы уже знаете, что, используя директиву #define, можно задавать константы. Например, если написать инструкцию #define PI 3.14, компилятор подставит значение3.14 на место всех встречающихся в программе констант PI. Директива #define предписывает компилятору заменить имя константы на то, что следует за этим именем.

Если после имени константы ввести какую-нибудь инструкцию, компилятор тоже произведет подстановку. Например, в следующей инструкции мы подставляем на место константы ENTER функцию printf(): #define ENTER printf(«Пожалуйста, введите число: «) Теперь, при необходимости отобразить сообщение, записанное в аргументе функции printf(), достаточно в соответствующем месте программы использовать инструкцию ENTER: #define ENTER printf(«Пожалуйста, введите число: «) main() < int age, size; ENTER; scanf(«%d», ENTER; scanf(«%d», > Рис.

7.9. Использование макроопределения При выполнении программы сообщение «Пожалуйста, введите число:» будет появляться на экране точно так же, как если бы в main() была полностью написана инструкция, содержащая функцию printf() (рис.7.9). Директивы, подобные той, которую мы только что рассмотрели, называются макроопределениями или макросами*.

Читайте также:
Программы для копирования файлов на планшет

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

Еще более мощным программным средством макроопределения делает то обстоятельство, что им можно передавать аргументы, так же, как функциям. В приведенной ниже программе, например, макрос CONVERT используется для перевода значения температуры из шкалы Фаренгейта в шкалу Цельсия: #define ENTER printf(«Пожалуйста, введите значение температуры: «) #define CONVERT(temp) (5.0 / 9.0) * (temp — 32) main() < float climate; ENTER; scanf(«%f», printf(«это соответствует %f по шкале Цельсияn», CONVERT(climate)); >* В литературе также используется термин макроподстановка. (Прим.перев.)

Рис.

7.10. Определение выражения как макроса Во второй директиве #define определяется макрос CONVERT, который требует передачи ему одного значения. Аргумент, принимаемый макросом CONVERT, подставляется в выражение (5.0 / 9.0) * (temp — 32) на место слова temp.

Вызов макроса осуществляется способом, аналогичным вызову функции, с использованием в качестве аргумента значения, которое мы хотим преобразовать (рис.7.10). Если в ответ на запрос введено значение212, происходит вызов CONVERT в функции printf() и расчет значения выражения (5.0 / 9.0) * (212 — 32). Использование макросов имеет те же преимущества, что и использование констант. Например, если вы захотите заменить стандартный запрос ввода данных, это делается простым внесением изменений в макроопределение ENTER в начале программы. Если вы сделали ошибку в формуле перевода температуры, вам придется отредактировать только одну строку программы, а не все места, где встречается имя макроопределения.

Источник: studfile.net

Потеря точности из Double во Float или «Куда пропадали копейки?»

Преобразование чисел из одного типа в другой обычно ведется таким образом, чтобы не потерять лишних чисел, т.е. из меньшего типа к более вместительному. Но что, если предыдущий разрабочик использовал конвертацию из Double во Float и стали пропадать копейки в отчетах?
В статье приводится изучение конвертации плавающих чисел в Java:

99999999.33333333 -> 100000000.0000000 98888888.33333333 -> 98888888.0000000 2974815.78000000 -> 2974815.7500000

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

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

public class Main < static void testDoubleToFloat(double d) < float f = (float) d; System.out.println(); System.out.println(String.format(«double %.10ft%s», d, Long.toBinaryString(Double.doubleToRawLongBits(d)))); System.out.println(String.format(«float %.10ft %s», f, Integer.toBinaryString(Float.floatToRawIntBits(f)))); >public static void main(String[] args) < System.out.println(String.format(«double: %.10f / %.10f», Double.MIN_VALUE, Double.MAX_VALUE)); System.out.println(String.format(«float: %.10f / %.10f», Float.MIN_VALUE, Float.MAX_VALUE)); /* По умолчанию, вычисления с плавающей точкой ведутся с помощью double. */ testDoubleToFloat(99999999.0 + 1.0 / 3.0); // Добавим периодичности testDoubleToFloat(98888888.0 + 1.0 / 3.0); // Вариант без округления девяток testDoubleToFloat(2974815.78); testDoubleToFloat(-2974815.78); >>
Результат выполнения
double: 0.0000000000 / 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0000000000 float: 0.0000000000 / 340282346638528860000000000000000000000.0000000000 double 99999999.3333333300 100000110010111110101111000001111111101010101010101010101010101 float 100000000.0000000000 1001100101111101011110000100000 double 98888888.3333333300 100000110010111100100111011001011100001010101010101010101010101 float 98888888.0000000000 1001100101111001001110110010111 double 2974815.7800000000 100000101000110101100100010111111100011110101110000101000111101 float 2974815.7500000000 1001010001101011001000101111111 double -2974815.7800000000 1100000101000110101100100010111111100011110101110000101000111101 float -2974815.7500000000 11001010001101011001000101111111

Одинаково для
/opt/jdk1.7/bin/java -version java version «1.7.0_25» Java(TM) SE Runtime Environment (build 1.7.0_25-b15) Java HotSpot(TM) 64-Bit Server VM (build 23.25-b01, mixed mode)
java -version java version «1.7.0_25» OpenJDK Runtime Environment (IcedTea 2.3.12) (7u25-2.3.12-4ubuntu3) OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode)

Читайте также:
Какие есть программы для делопроизводства

Способ конвертации

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

public class Main < static void testDoubleToFloat(double d) < float f = (float) d; Float f2 = new Float(d); float f3 = Float.parseFloat(new Double(d).toString()); float f4 = Float.parseFloat(String.format(«%.10f», d)); System.out.println(); System.out.println(String.format(«double %.10ft%s», d, Long.toBinaryString(Double.doubleToRawLongBits(d)))); System.out.println(String.format(«float %.10ft %s», f, Integer.toBinaryString(Float.floatToRawIntBits(f)))); System.out.println(String.format(«Float %.10ft %s», f2, Integer.toBinaryString(Float.floatToRawIntBits(f2)))); System.out.println(String.format(«float %.10ft %s», f3, Integer.toBinaryString(Float.floatToRawIntBits(f3)))); System.out.println(String.format(«float %.10ft %s», f4, Integer.toBinaryString(Float.floatToRawIntBits(f4)))); >public static void main(String[] args) < System.out.println(String.format(«double: %.10f / %.10f», Double.MIN_VALUE, Double.MAX_VALUE)); System.out.println(String.format(«float: %.10f / %.10f», Float.MIN_VALUE, Float.MAX_VALUE)); testDoubleToFloat(99999999.0 + 1.0 / 3.0); testDoubleToFloat(98888888.0 + 1.0 / 3.0); testDoubleToFloat(2974815.78); testDoubleToFloat(-2974815.78); >>
Результат выполнения
double: 0.0000000000 / 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0000000000 float: 0.0000000000 / 340282346638528860000000000000000000000.0000000000 double 99999999.3333333300 100000110010111110101111000001111111101010101010101010101010101 float 100000000.0000000000 1001100101111101011110000100000 Float 100000000.0000000000 1001100101111101011110000100000 float 100000000.0000000000 1001100101111101011110000100000 float 100000000.0000000000 1001100101111101011110000100000 double 98888888.3333333300 100000110010111100100111011001011100001010101010101010101010101 float 98888888.0000000000 1001100101111001001110110010111 Float 98888888.0000000000 1001100101111001001110110010111 float 98888888.0000000000 1001100101111001001110110010111 float 98888888.0000000000 1001100101111001001110110010111 double 2974815.7800000000 100000101000110101100100010111111100011110101110000101000111101 float 2974815.7500000000 1001010001101011001000101111111 Float 2974815.7500000000 1001010001101011001000101111111 float 2974815.7500000000 1001010001101011001000101111111 float 2974815.7500000000 1001010001101011001000101111111 double -2974815.7800000000 1100000101000110101100100010111111100011110101110000101000111101 float -2974815.7500000000 11001010001101011001000101111111 Float -2974815.7500000000 11001010001101011001000101111111 float -2974815.7500000000 11001010001101011001000101111111 float -2974815.7500000000 11001010001101011001000101111111

Как можно убедиться, выражения «new Float(d)» и «(float) d» дают одинаковый результат, т.к. первое использует второе:

/* * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ . public final class Float extends Number implements Comparable < . public Float(double value) < this.value = (float)value; >. >

Если разбираться с функцией Float.parseFloat, то она отсылает нас через несколько других функций до следующей строки:

return (float)Double.longBitsToDouble( lbits );

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

float f = (float) d;

Форматы хранения float и double

В каждом примере мы вызывали функции Long.toBinaryString для Double и Integer.toBinaryString для Float, чтобы продемонстрировать форматы низкоуровневого хранения созданных переменных. Прекрасная статья об этом уже была написана (Что нужно знать про арифметику с плавающей запятой, которая стала отличным переводом английской вики, где хорошо рассказано и про двойную точность), поэтому здесь мы рассмотрим только то, касается округления.
Представленные выше программы вернули следующие результаты:

double 2974815.7800000000 100000101000110101100100010111111100011110101110000101000111101 float 2974815.7500000000 1001010001101011001000101111111

Тип double занимает 64 бита, а тип float 32, но мы видим 63 и 31 знак — это издержка реализации вывода, который заканчивается, когда остаются только нули. Следовательно, эти числа должны выглядеть так:

double 2974815.7800000000 0 10000010100 0110101100100010111111100011110101110000101000111101 float 2974815.7500000000 0 10010100 01101011001000101111111

  • 1-ые биты 0 => знаки положительные
  • Далее экспоненты: 0100000101002-102310=21

Вывод

Таким образом, конвертация из формата большей точности может привести к нетривиальным потерям достоверности используемых чисел. Что же касается непосредственно Java, то в ней лучше использовать тип Double, т.к. работа с Float чревата конвертациями из Double с потерями. А для хранения денег использовать BigDecimal, чтобы не терять копейки.

P.S.

  • Fix Float.parseFloat to round correctly and preserve monotonicity. — мы-то с Вами уже разобрались, почему такое происходит
  • Direct String-to-float conversion does not preserve monotonicity — то же самое

CUSTOMER SUBMITTED WORKAROUND:
Avoiding direct String-to-float conversion by using intermediate doubles.

Предложенные решения

    Хранить деньги в банкахBigDecimal:

BigDecimal bg = new BigDecimal(«2974815.78»); System.out.println(bg);
class Currency < long value; . public double asDouble() < return value / 100.0; >. . >

Источник: habr.com

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru